Skynet专题之:线程

JavenLaw

在全局初始化skynet_globalinit()函数中,涉及到了线程相关的知识,现在将详细地了解线程的相关知识


pthread_key_t

pthread_key_t 是POSIX线程库中用于表示线程特定数据键的数据类型

在多线程程序中,线程特定数据允许每个线程拥有自己独立的数据副本,而不会互相干扰 这对于需要在线程之间共享数据的场景非常有用

对于线程特定数据的操作的例子,详细地可以看 skynet_server.c文件中对于 skynet_globalinit 的初始化

常用相关函数有:

​ pthread_key_create():创建一个线程本地存储的键

​ pthread_setspecific():为当前线程设置线程本地存储键的值

​ pthread_getspecific():获取当前线程特定键的值

​ pthread_key_delete():删除一个线程本地存储的键

// pthread_key_t 是一个抽象的类型,实际上是一个整数或指针,用于标识线程特定数据键
// 它在创建线程特定数据键时由 pthread_key_create() 函数填充,并在后续的线程特定数据操作中使用
// 在使用 pthread_key_t 类型时,通常需要先声明一个 pthread_key_t 变量
// 然后通过 pthread_key_create() 函数创建线程特定数据键,并将键的值存储在该变量中
// 最后可以使用这个键来获取,设置线程特定数据副本

// 特别说明:网页显示问题,以下代码的 预处理语句 都省略了 #
// 使用例子:
include <pthread.h>
include <stdio.h>

pthread_key_t key;

void destructor(void* data) {
    printf("Destructor called for thread %lu\n", pthread_self());
    free(data);
}

void* thread_function(void* arg) {
    int* thread_specific_data = malloc(sizeof(int));
    if (thread_specific_data == NULL) {
        perror("Failed to allocate memory");
        pthread_exit(NULL);
    }
    *thread_specific_data = (int)(size_t)arg;

    // 将线程特定数据与键关联
    if (pthread_setspecific(key, thread_specific_data) != 0) {
        perror("Failed to set thread specific data");
        free(thread_specific_data);
        pthread_exit(NULL);
    }

    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // 创建线程特定数据键
    if (pthread_key_create(&key, destructor) != 0) {
        perror("Failed to create key");
        return 1;
    }

    // 创建两个线程
    if (pthread_create(&thread1, NULL, thread_function, (void*)(size_t)1) != 0) {
        perror("Failed to create thread1");
        return 1;
    }
    if (pthread_create(&thread2, NULL, thread_function, (void*)(size_t)2) != 0) {
        perror("Failed to create thread2");
        return 1;
    }

    // 等待两个线程结束
    if (pthread_join(thread1, NULL) != 0) {
        perror("Failed to join thread1");
        return 1;
    }
    if (pthread_join(thread2, NULL) != 0) {
        perror("Failed to join thread2");
        return 1;
    }

    // 删除线程特定数据键
    if (pthread_key_delete(key) != 0) {
        perror("Failed to delete key");
        return 1;
    }

    printf("Main thread finished execution.\n");

    return 0;
}

// 特别注意:
// pthread_key_t 变量只是一个用于存储线程特定数据键的标识符,它本身并不存储实际的数据
// 线程特定数据的副本是通过 pthread_setspecific() 和 pthread_getspecific() 函数与键关联的


pthread_key_create()

pthread_key_create() 用于创建线程特定数据(Thread-Specific Data,简称 TSD)的键

// 函数原型
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

// 参数
// key: 指向 pthread_key_t 类型变量的指针,用于存储新创建的键
// destructor: 指向一个析构函数的指针
// 当线程退出时,如果该键的值非空,则调用此析构函数来释放键关联的数据
// 如果不需要析构函数,可以传递 NULL
//
// 返回值
// 0: 表示函数成功执行
// 非零值: 表示函数执行失败,返回错误码


pthread_setspecific()

pthread_setspecific() 用于将线程特定数据的副本与线程特定数据键关联起来

// 函数原型:
int pthread_setspecific(pthread_key_t key, const void *value);

// 参数
// key: 由 pthread_key_create 创建的键
// value: 要与键关联的线程特定数据的指针
// 
// 返回值
// 0: 表示函数成功执行
// 非零值: 表示函数执行失败,返回错误码


pthread_getspecific()

pthread_getspecific() 用于获取与线程特定数据键关联的线程特定数据副本

// 函数原型:
void *pthread_getspecific(pthread_key_t key);

// 参数
// key: 由 pthread_key_create 创建的键,用于检索线程特定数据
// 
// 返回值
// 返回与 key 关联的线程特定数据的指针。如果未设置数据,则返回 NULL


pthread_key_delete()

pthread_key_delete() 用于删除线程特定数据键

// 函数原型:
int pthread_key_delete(pthread_key_t key);

// 参数
// key: 要删除的线程特定数据键
// 
// 返回值
// 0: 表示函数成功执行
// 非零值: 表示函数执行失败,返回错误码


pthread_t

pthread_t 是POSIX线程库中表示线程的类型。每个 pthread_t 变量代表一个独立的线程,用于表示线程和管理线程

对于线程创建管理的例子,详细地可以看 skynet_start.c文件中对于 create_thread 的使用

常用相关函数有:

​ pthread_create():创建一个新线程

​ pthread_join():等待指定线程结束

​ pthread_detach():将线程设置为分离状态,结束后自动释放资源

​ pthread_exit():结束当前线程

​ pthread_self():获取当前线程的 pthread_t 标识

// 特别说明:网页显示问题,以下代码的 预处理语句 都省略了 #
// 使用例子:
include <pthread.h>
include <stdio.h>
include <stdlib.h>

void* thread_function(void* arg) {
    printf("Hello from the thread!\n");
    return NULL;
}

int main() {
    pthread_t thread;
    if (pthread_create(&thread, NULL, thread_function, NULL) != 0) {
        perror("pthread_create");
        return 1;
    }

    if (pthread_join(thread, NULL) != 0) {
        perror("pthread_join");
        return 1;
    }

    printf("Thread has finished execution.\n");
    return 0;
}


pthread_create()

pthread_create() 用于创建一个新的线程

// 函数原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

// 参数
// thread: 指向 pthread_t 类型变量的指针,用于存储新创建线程的标识符
// attr: 指向 pthread_attr_t 类型变量的指针,用于设置新线程的属性。如果为 NULL,则使用默认线程属性
// start_routine: 指向新线程将要运行的函数的指针。该函数的返回类型为 void *,参数类型为 void *
// arg: 传递给 start_routine 函数的参数。可以是 NULL 或指向任何数据的指针
// 
// 返回值
// 0: 表示线程创建成功
// 非零值: 表示线程创建失败,返回错误码


pthread_join()

pthread_join() 用于等待指定的线程终止

// 函数原型
int pthread_join(pthread_t thread, void **retval);

// 参数
// thread: 这是一个 pthread_t 类型的变量,表示需要等待的线程的标识符
// retval: 指向指针的指针,用于接收目标线程的返回值。如果不需要获取线程的返回值,可以传递 NULL
// 
// 返回值
// 0: 表示函数成功执行。
// 非零值: 表示函数执行失败,返回错误码


pthread_detach()

pthread_detach() 用于将一个线程的状态设置为分离(detached)状态

处于分离状态的线程在结束时,其资源会被自动回收,而不需要其他线程调用 pthread_join 来显式地等待它结束

这个功能对于不需要获取线程返回值或等待线程结束的情况非常有用

// 函数原型
int pthread_detach(pthread_t thread);

// 参数
// thread: 这是一个 pthread_t 类型的变量,表示需要设置为分离状态的线程的标识符
//
// 返回值
// 0: 表示函数成功执行
// 非零值: 表示函数执行失败,返回错误码


pthread_self()

pthread_self() 用于获取调用线程的线程标识符

// 函数原型
pthread_t pthread_self(void);

// 参数
// pthread_self 没有参数
//
// 返回值
// 返回调用线程的线程标识符,类型为 pthread_t


pthread_exit()

pthread_exit() 用于显式地终止当前线程

// 函数原型
void pthread_exit(void *retval);

// 参数
// retval: 指向线程的返回值,可以是任意类型的指针
//  	   如果线程被 pthread_join 等待,则这个值可以被接收。如果不需要返回值,可以传递 NULL
//
// 返回值
// pthread_exit 没有返回值,因为调用这个函数后,当前线程会立即终止


pthread_mutex_t

pthread_mutex_t 是POSIX线程库(pthreads)中的互斥锁类型,用于实现线程间的互斥

互斥锁(Mutex)是一种同步原语,用于防止多个线程同时访问共享资源,从而避免数据竞争和不一致性

对于线程互斥锁操作的例子,详细地可以看 skynet_start.c文件中对于 monitor 结构体中 pthread_mutex_t 的使用

常用相关函数有:

​ pthread_mutex_init():初始化一个互斥锁

​ pthread_mutex_lock():锁定互斥锁

​ pthread_mutex_trylock():尝试锁定互斥锁

​ pthread_mutex_unlock():解锁互斥锁

​ pthread_mutex_destroy():销毁互斥锁

// 特别说明:网页显示问题,以下代码的 预处理语句 都省略了 #
// 使用例子:
include <pthread.h>
include <stdio.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int shared_variable = 0;

void* thread_function(void* arg) {
    for (int i = 0; i < 1000000; ++i) {
        pthread_mutex_lock(&mutex);
        ++shared_variable;
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // 创建两个线程
    pthread_create(&thread1, NULL, thread_function, NULL);
    pthread_create(&thread2, NULL, thread_function, NULL);

    // 等待两个线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 输出共享变量的值
    printf("Shared variable: %d\n", shared_variable);

	return 0;
}


pthread_mutex_init()

pthread_mutex_init() 是用于初始化互斥锁(mutex)的函数

// 函数原型
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

// 参数
// mutex: 指向要初始化的互斥锁的指针
// attr: 用于指定互斥锁属性的指针,通常为 NULL 表示使用默认属性
// 
// 返回值
// 返回值为 0 表示成功初始化互斥锁
// 返回值为非零表示初始化失败,可能是由于内存分配失败或者参数错误等原因


pthread_mutex_lock()

pthread_mutex_lock() 用于锁定互斥锁(mutex)的函数

当一个线程尝试锁定一个已经被另一个线程锁定的互斥锁时,pthread_mutex_lock 会使得线程阻塞,直到它可以安全地锁定该互斥锁为止

// 函数原型
int pthread_mutex_lock(pthread_mutex_t *mutex);

// 参数
// mutex: 指向要锁定的互斥锁的指针
// 
// 返回值
// 返回值为 0 表示成功锁定互斥锁
// 返回值为非零表示锁定失败,可能是由于参数错误等原因


pthread_mutex_trylock()

pthread_mutex_trylock() 用于尝试锁定互斥锁(mutex)的函数

与 pthread_mutex_lock() 不同的是,它是非阻塞的

即如果互斥锁已经被其他线程锁定,则 pthread_mutex_trylock 会立即返回,而不是阻塞等待互斥锁可用

// 函数原型
int pthread_mutex_trylock(pthread_mutex_t *mutex);

// 参数
// mutex: 指向要尝试锁定的互斥锁的指针
// 
// 返回值
// 返回值为 0 表示成功锁定互斥锁
// 返回值为 EBUSY 表示互斥锁已被其他线程锁定,无法尝试锁定
// 返回值为其他非零值表示函数执行失败,可能是由于参数错误等原因


pthread_mutex_unlock()

pthread_mutex_unlock() 用于解锁互斥锁(mutex)的函数

当一个线程完成对共享资源的访问后,应该使用 pthread_mutex_unlock 来释放互斥锁,以允许其他线程访问该共享资源

// 函数原型
int pthread_mutex_unlock(pthread_mutex_t *mutex);

// 参数
// mutex: 指向要解锁的互斥锁的指针
// 
// 返回值
// 返回值为 0 表示成功解锁互斥锁
// 返回值为非零表示解锁失败,可能是由于参数错误等原因


pthread_mutex_destroy()

pthread_mutex_destroy() 用于销毁互斥锁(mutex)的函数

在不再需要互斥锁时,应该使用 pthread_mutex_destroy 来释放相关的资源

// 函数原型
int pthread_mutex_destroy(pthread_mutex_t *mutex);

// 参数
// mutex: 指向要销毁的互斥锁的指针
// 
// 返回值
// 返回值为 0 表示成功销毁互斥锁
// 返回值为非零表示销毁失败,可能是由于参数错误等原因


pthread_cond_t

pthread_cond_t 是POSIX线程库中用于条件变量(condition variable)的数据类型

条件变量允许线程在满足特定条件之前等待,并在条件满足时被通知继续执行

通常与互斥锁一起使用,用于线程间的同步

对于线程互斥锁操作的例子,详细地可以看 skynet_start.c文件中对于 monitor 结构体中 pthread_cond_t的使用

常用相关函数有:

​ pthread_cond_init():初始化条件变量

​ pthread_cond_wait(): 等待条件变量的信号

​ pthread_cond_signal():发送信号给一个等待条件变量的线程

​ pthread_cond_broadcast():发送信号给所有等待条件变量的线程

​ pthread_cond_destroy():销毁条件变量

// 特别说明:网页显示问题,以下代码的 预处理语句 都省略了 #
include <pthread.h>
include <stdio.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int flag = 0;

void* thread_function(void* arg) {
    pthread_mutex_lock(&mutex);
    while (flag == 0) {
        pthread_cond_wait(&cond, &mutex);
    }
    printf("Thread %lu: Condition signaled.\n", pthread_self());
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    // 创建线程
    pthread_create(&thread1, NULL, thread_function, NULL);
    pthread_create(&thread2, NULL, thread_function, NULL);

    // 发送信号给等待条件变量的线程
    pthread_mutex_lock(&mutex);
    flag = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);

    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

	return 0;
}


pthread_cond_init()

pthread_cond_init() 用于初始化条件变量(condition variable)的函数

// 函数原型
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

// 参数
// cond:要初始化的条件变量
// attr:条件变量的属性,通常传入 NULL 表示使用默认属性
// 
// 返回值
// 成功初始化返回0
// 失败返回对应的错误码


pthread_cond_wait()

pthread_cond_wait() 用于等待条件变量的函数之一

它使线程进入睡眠状态,等待条件变量被其他线程发送信号(signal)或广播(broadcast)

// 函数原型
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

// 参数
// cond:要等待的条件变量
// mutex:与条件变量关联的互斥锁。在调用此函数前,必须使用 pthread_mutex_lock 锁定该互斥锁
// 
// 返回值
// 函数成功时不返回任何值
// 失败返回对应的错误码


pthread_cond_signal()

pthread_cond_signal 用于发送信号给等待条件变量的线程的函数之一

它通知等待在条件变量上的一个线程,使其从等待状态返回继续执行

// 函数原型
int pthread_cond_signal(pthread_cond_t *cond);

// 参数
// cond:要发送信号的条件变量
//
// 返回值
// 函数成功时返回 0
// 失败返回对应的错误码

pthread_cond_broadcast()

pthread_cond_broadcast 用于向等待在条件变量上的所有线程发送信号的函数

它会唤醒所有等待线程,使它们可以从 pthread_cond_wait 中返回并继续执行

// 函数原型
int pthread_cond_broadcast(pthread_cond_t *cond);

// 参数
// cond:要广播信号的条件变量
// 
// 返回值
// 函数成功时返回 0
// 失败返回对应的错误码