🐧【Linux】pthread线程管理
线程状态
线程有两种状态:join和detached
- join:默认状态,父线程原则上要阻塞自身,等待子线程完成后再继续执行(有时代码允许你不这样做,但是那将会导致子线程的资源不会被回收,造成内存泄漏),并要主动在子线程结束后回收线程。
- detached:子线程脱离父线程运行,成为一个守护线程。在自身线程结束时候就会自动回收所占用的资源
joinable后,可以调用pthread_detach()使之成为detached。但是相反的操作则不可以。 如果线程已经调用pthread_join()后,则再调用pthread_detach()则不会有任何效果。
线程退出
线程正常退出有三种路径:
- return,主动返回
- pthread_exit(),主动线程退出
- pthread_cancel(), 被同进程的其他线程取消
pthread_cancel()返回时,线程未必已经取消,可能仅仅将请求发送给目标线程,而目标线程目前没有到达取消点,如果要知道线程在何时中止,就需要在取消它之后调用pthread_join()。 如果线程是detached,则调用pthread_join()会返回错误值或不起作用。
资源回收
Linux系统中程序的线程资源是有限的,表现为对于一个程序其能同时运行的线程数是有限的。而默认的条件下,一个线程结束后,其对应的资源不会被释放。
解决这个问题,有2种方式,系统自动释放线程资源,或者由另一个线程释放该线程资源。
- 如果想在线程结束时,由系统释放线程资源,则需要设置线程属性为detach。
- 如果由另外线程释放,则使用
pthread_join()pthread_join函数会阻塞等待指定线程退出,然后回收资源,这样就有同步的功能,使一个线程等待另一个线程退出,然后才继续运行
一个线程不能被多个线程等待。否则第一个收到信号的线程成功返回。其余调用pthread_join的线程返回错误码
1. 实际上回收的是哪些资源?
1)子线程创建时从父线程copy出来的栈内存
线程退出有多种方式,如return,pthread_exit,pthread_cancel等;线程分为 joinable 和 detached 两种,如果没有在创建线程时设置线程的属性为PTHREAD_CREATE_DETACHED,则线程默认是joinable 。joinable 线程在线程退出后不会立即释放资源,必须要调用pthread_join来显式的结束线程。detached 的线程在线程退出时系统会自动回收资源。
对于这类资源,主要通过 【设置分离属性 】和 pthread_join() 两种方法来处理。
其中【设置分离属性 】又可以分别用pthread_attr_setdetachstat()和pthread_detach()来处理。
2)子线程内部单独申请的堆内存(malloc、realloc、calloc)和锁资源mutex
处于挂起状态的取消请求(即加锁之后,解锁之前),线程在执行到取消点时如果只是草草收场,这会将共享变量以及pthreads对象(例如互斥量)置于一种不一致状态,可能导致进程中其他线程产生错误结果、死锁,甚至造成程序崩溃。
为避免这一问题:使用清理函数pthread_cleanup_push()和pthread_cleanup_pop()来处理。
2. 分离线程的几种方法
- pthread_attr_setdetachstat(&attr, PTHREAD_CREATE_DETACHED);
- 在线程中调用pthread_detach(pthread_self());
- 主线程中调用pthread_detach(pid),pid为子线程的线程号
设置为分离的线程不能调用pthread_join,调用后会出错
3.可结合的线程的几种退出方式
- 子线程使用return或pthread_exit退出,主线程中使用pthread_join回收线程
- 主线程中调用pthread_cancel,然后调用pthread_join回收线程
pthread_cancel函数执行的条件: 1、产生了系统调用(sleep、read、write、open等系统接口) 2、pthread_testcancel();//设置取消点
linux线程执行和windows不同,pthread有两种状态joinable状态和unjoinable状态,如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。
若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
注意
1. exit()和pthread_exit()的区别
exit()函数指的是结束进程,在任何一个线程中调用,则进程都会被结束
pthread_exit()只是结束当前进程,即使在主线程中执行,也不会影响进程中的其他线程。直到所有线程结束,进程才会终止。
2. pthread_attr_destroy
Q: 退出线程或线程创建失败时,为什么要调用pthread_attr_destroy?
A:如下
- 线程的属性有很多,如果想要设置线程属性功能,就必须诞生一个线程的属性结构体变量(pthread_attr_t),并且用pthread_attr_init去初始化这个变量,当用完之后用线程的pthread_attr_destroy去销毁掉线程的属性结构体
- pthread_attr_destory() 函数的意思是销毁线程的属性结构体,使它未初始化不能再次使用
- 使用该属性对象创建线程后,就可以把属性对象销毁了,不会影响已经用它创建的线程。
3. pthread_exit() 和 return 的区别
目前我已知的一个区别场景是,如果在main函数中调用return,而子线程还没有执行完时,进程会退出。而main函数执行pthread_exit(),即使main函数走完了,也不影响子线程的执行。
总结是在线程使用中推荐用pthread_exit()替代return。
4. 线程的大小
pthread_create创建线程时,若不指定分配堆栈大小,系统会分配默认值,查看默认值方法如下:
# ulimit -s
8192
#
上述表示为8M;单位为KB。
相关笔记
- 线程
- 锁和条件变量
- Linux的进程、线程、文件描述符是什么