线程状态

线程有两种状态:join和detached

  • join:默认状态,父线程原则上要阻塞自身,等待子线程完成后再继续执行(有时代码允许你不这样做,但是那将会导致子线程的资源不会被回收,造成内存泄漏),并要主动在子线程结束后回收线程。
  • detached:子线程脱离父线程运行,成为一个守护线程。在自身线程结束时候就会自动回收所占用的资源

joinable后,可以调用pthread_detach()使之成为detached。但是相反的操作则不可以。 如果线程已经调用pthread_join()后,则再调用pthread_detach()则不会有任何效果。

线程退出

线程正常退出有三种路径:

  1. return,主动返回
  2. pthread_exit(),主动线程退出
  3. 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的进程、线程、文件描述符是什么