以下内容引述至《Linux/Unix系统编程手册》
线程
Pthread API 定义了一干数据类型
数据类型 | 描述 |
---|---|
pthread_t | 线程ID |
pthread_mutex_t | 互斥对象 |
pthread_mutexattr_t | 互斥属性对象 |
pthread_cond_t | 条件变量 |
pthread 函数返回值
从系统调用和库函数中返回状态,传统的做法是:返回0表示成功,返回-1表示失败,并设置errno以标识错误原因。pThread API则反其道行之,返回0表示成功,返回一正值表示失败。这个值与errno中的值含义相同。
编译
编译pThread API的程序时,需要设置 cc -pthread的编译选项,使用该选项的效果如下:
- 定义_REENTRANT预处理宏,这会公开对少数可重入函数的声明;
- 程序会与libpthread进行连接(等价于-lpthread)
线程创建
#include <pthread.h>
int pthread_create(pthread_t* thread, const pthread_attr_t *attr,
void* (*start)(void*), void* arg);
将参数arg声明为void类型,意味着可以将指向任意对象的指针传递给start()函数;
严格来说,对于int与void之间相互强制转换的后果,C语言标准未加以定义。start的返回值类型为void*,对其使用方式与参数arg相同。
线程终止
线程终止的方式
- 线程start函数执行return语句并返回指定值
- 线程调用pthread_exit()
- 调用pthread_cancel取消线程
- 任意线程调用了exit(),或者主线程执行了return语句
#include <pthread.h>
void pthread_exit(void *retval);
参数retval指定了线程的返回值。
如果主线程调用了pthread_exit(),而非调用exit()或者执行return语句,那么其他线程将继续运行。
线程ID
进程内部每个线程有一个唯一标志,线程ID。但是多进程下,线程ID不一定唯一。
线程ID返回给pthread_create()的调用者,一个线程可以通过pthread_self()来获取自己线程的ID
#include <pthread.h>
pthread_t pthread_self(void);
int pthread_equal(pthread_t t1, pthread_t t2);
不同于gettid函数,gettid是内核分配的数字
连接已终止的线程
函数pthread_join等待由thread标志的线程终止(如果线程已经终止,pthread_join()会立即返回)
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
如果向pthread_join传入一个之前已经连接过的线程ID,将会导致无法预知的行为。
若线程未分离,则必须使用pthread_join()来进行连接。如果未能连接,那么线程终止时将产生僵尸线程,类似于僵尸进程。除了浪费系统资源外,僵尸线程若积累过多,应用将再也无法创建新的线程。
pthread_join与waitpid的差别
- 线程之间的关系是对等的,进程中的任意线程均可以调用pthread_join与该进程的任何其他线程连接起来。
- 无法“连接任意线程”也不能以非阻塞方式进行连接,使用条件变量可以实现类似的功能。
PS: join(self)会发生死锁
if(!pthread_equal(tid, pthread_self()){
pthread_join(tid, nullptr);
}
线程分离
默认情况下,线程是可连接的(joinable)
当不关心线程的返回状态时,只是希望系统在线程终止的时候能够自动清理并移除,可以调pthread_detach并向thread参数传入指定线程的标识符,将线程标志于分离的状态
#include <pthread.h>
int pthread_detach(pthread_t thread);
其他线程调用了exit(),或者主线程执行了return语句时,即便遭到分离的线程也还是会收到影响。不管线程是分离还是可连接状态,进程的所有线程会立即终止。