线程管理
一、基本概念
1、线程是进程的执行路线,它是进程内部的控制序列,或者说线程是进程的一部分(进程是一个资源单位,线程是执行单位,线程是进程的一部分,负责真正的执行)
2、线程是轻量级的,没有自己独立的代码段、数据段、bss段、堆、环境变量、命令行参数、文件描述符、信号处理函数、当前目录信息等资源
3、线程有自己独立的栈内存、线程ID、错误码、信号屏蔽掩码
4、一个进程中可以包含多个线程(多个执行路线),但是至少有一个线程在活动,称为主线程
5、ps -T -p <pid> 查看pid进程中的线程情况 或者htop命令也可以查看
6、线程是进程的实体,可以当做系统独立的任务调度和分配的基本单位
7、线程有不同的状态、属性,系统提供了线程的控制接口,例如:创建、销毁、控制
8、进程中的所有线程同在一个虚拟地址空间中,进程中的所有资源对于线程而言都是共享的,因此当多个线程协同工作时需要解决资源竞争问题(加锁)
9、线程的系统开销很小、任务切换快,多个线程之间不需要数据交换、因此不需要类似于XSI的通信机制,因此使用线程简单而高效
10、线程之间有优先级的差异
二、POSIX线程
1、早期的UNIX和Linux系统没有线程概念,微软的Windows系统首先使用的线程,之后UNIX和Linux系统也逐渐增加了线程
2、早期各个厂商有自己私有的线程库,而且接口的实现差异较大,不利于移植,世界标准化组织与1995年,制定了统一的线程接口标准规范,遵循该标准的线程称为POSIX线程,简称pthread
3、pthread线程包含一个头文件 <pthread.h> 和一个共享库libpthread.so
-pthread
三、线程管理
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
功能:创建新线程
thread:输出型参数,用于获取线程ID
attr:用于设置线程属性,一般写NULL即可
start_routine:线程的入口函数,相当于主线程的main函数
arg:传递给start_routine入口函数的参数
返回值:成功返回0,失败返回错误编码
注意:入口函数的参数。返回值要确保它的可持续性,因此不太适合使用栈内存,可以考虑堆内存、全局变量
int pthread_join(pthread_t thread, void **retval);
功能: 等待线程的结束,并获取该线程结束时入口函数的返回值,并释放线程资源
thread:要等待的线程ID
retval:用于存储线程结束时返回值的地址,拿到返回值变量本身
返回值:成功返回0,失败返回错误编码
pthread_t pthread_self(void);
功能:获取当前线程的线程ID
int pthread_equal(pthread_t t1, pthread_t t2);
功能:比较两个线程ID是否一致
返回值:一致返回非零值,不一致返回0
注意:在个别操作系统下,pthread_t 是以结构实现的,大部分是以 unsigned long 呈现,为了可移植性,不能直接使用 == 比较
pthread_t tid;// 不要初始化 提高可移植性
四、线程的执行轨迹
同步方式:默认 (可结合状态)
创建子线程后,主线程通过调用pthread_join函数等待子线程终止,并释放线程资源
异步方式:(分离状态)
无需创建等待(如果创建者调用pthread_join函数会立即返回),线程结束时由系统释放资源
原因:为了避免线程资源泄漏,每个可结合状态的线程必须显示地调用pthread_join来回收资源或者将其变成分离状态
int pthread_detach(pthread_t thread);
功能:让已创建的thread标识的线程与创建者线程分离
pthread_detach两种用法:
1、主线程中调用 pthread_detach(tid)
2、新线程中调用 pthread_detach(pthread_self())
注意:无论哪种分离写法,都必须发生在pthread_join之前,否则join一旦进入等待,再分离也不会退出等待
五、线程的终止
1、线程执行了入口函数的最后一行代码 包括return语句
2、线程执行了pthread_exit函数
void pthread_exit(void *retval);
功能:结束当前线程
retval:等同于return 后面的val
注意:从表面上看当主线程结束后,子线程会跟着一起结束,就会误以为主线程的结束影响了子线程的结束,但是实际上子线程之所以结束是因为主线程执行了main函数中隐藏的return语句,
导致了整个进程结束,所以进程中所有的线程才会随之结束。
如果主线程调用pthread_exit自杀,这样就没有线程取执行main函数的return语句,进程就不会提前结束,子线程就不受影响了
总结:主线程结束不会影响子线程的执行
3、如果所在的进程结束,所有的线程都随之结束
4、向指定线程发送取消请求
int pthread_cancel(pthread_t thread);
功能:向指定线程发送取消请求,默认情况下会响应请求
int pthread_setcancelstate(int state, int *oldstate);
功能:设置本线程是否响应取消请求,并获取之前的状态
PTHREAD_CANCEL_ENABLE 允许响应
PTHREAD_CANCEL_DISABLE 禁止响应
int pthread_setcanceltype(int type, int *oldtype);
功能:设置延迟响应
PTHREAD_CANCEL_DEFERRED 延迟响应 在接收到取消请求后不立即响应,而是等待合适的时间再响应
PTHREAD_CANCEL_ASYNCHRONOUS 立即响应
六、线程属性
pthread_attr_t
typedef struct
{
int detachstate; 线程的分离状态
int schedpolicy; 线程调度策略 先进先出 轮询
struct sched_param schedparam; 线程的调度参数
int inheritsched; 线程的继承性
int scope; 线程的作用域
size_t guardsize; 线程栈末尾的警戒缓冲区大小
int stackaddr_set; 线程栈的设置
void * stackaddr; 线程栈的位置
size_t stacksize; 线程栈的大小
}pthread_attr_t;
在/usr/include/i386-linux-gnu/bits/pthreadtypes.h这个文件中找到关于pthread_attr_r的定义,但是该定义不是真的线程属性结构体的定义,真实的定义应该如上所示,
大概是因为pthread不想让用户看到它对pthread_attr_t的实现,希望用户借助它提供的接口函数进行获取、设置属性