线程的创建及终止
线程 ID
线程 ID 只在它所属的进程环境有效,并用 pthread_t 数据类型来表示,实现的时候可以用一个结构来代表 pthread_t 数据类型,所以在可移植的操作系统实现不能把它当做为整数来处理。因此必须使用函数(pthread_equal)来对两个线程 ID 进行比较。可以用 pthread_self 获取自身线程 ID 。
线程的创建
线程创建时并不能保证哪个线程先运行 : 是新创建的线程还是调用线程。新创建的线程可以访问进程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的未决信号集被清除。
errno
注意 pthread 函数在调用失败时通常会返回错误代码,它们并不像其他的 POSIX 函数一样设置 errno。每个线程都提供 errno 的副本,这只是为了与使用 errno 的现有函数兼容。在线程中,从函数中返回的错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局状态,因而可以把错误的范围限制在引起出错的函数中。
线程的终止
如果进程中的任一线程调用了 exit,_Exit,或者_exit,那么整个进程就会终止。于此类似,如果信号的默认动作是终止进程,那么,把该信号发送到线程会终止整个进程。
单个线程终止三种方式
-
- 线程只是从启动例程中返回,返回值是线程的退出码。
- 线程可以被同一进程的其他线程取消(线程可以通过 pthread_cancel 函数来请求取消同一进程中的其他线程,默认情况下,pthread_cancel 函数会使得指定的线程的行为表现为如同调用了参数为 PTHREAD_CANCELED 的 pthread_exit 函数。但是线程可以选择忽略取消方式或是控制取消方式,,pthread_cancel 并不等待线程终止,它仅仅提交请求)。
- 线程调用 pthread_ext (在 pthread_exit 可以在退出的时候可以传递一些信息,那这些信息可以用 pthread_join 函数获得,那么调用 pthread_join 函数将会一直阻塞,直到指定的线程调用 pthread_exit)。
在默认情况下,线程的终止状态会保存到该线程调用 pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即被回收。当线程被分离时,并不能用 pthread_join 函数等待它的终止状态。对分离状态的线程进行 pthread_join 的调用可以产生失败,返回 EINVAL。pthread_detach 调用可以用于使线程进入分离状态。
资料:
-
- pthread_join 监控线程退出的状态 http://www.cnblogs.com/shishuirunian/archive/2012/05/18/2508070.html
- POSIX线程的创建与取消—pthreads线程库实例笔记1 http://www.cnblogs.com/blueclue/archive/2010/06/11/1754899.html
- POSIX线程的创建与取消—pthreads线程库实例笔记2 http://www.cnblogs.com/blueclue/archive/2010/07/02/1758301.html
- pthread_join: http://www.cnblogs.com/dongzhiquan/archive/2012/02/16/2355032.html
线程清理处理程序
线程可以安排它退出时需要的函数,这与进程可以用 atexit 函数安排进程退出时需要调用的函数是类似的。这样的函数称为线程清理处理程序。线程可以建立多个清理处理程序,处理程序记录在栈中,也就是说它们的执行顺序与它们注册时的顺序相反
-
- pthread_cleanup_push(..) 将线程清理处理程序压栈
- pthread_cleanup_pop(..) 将线程清理处理程序出栈
当线程执行以下动作时调用清理函数,调用参数 arg,清理函数 rtn 的调用顺序是由 pthread_cleanup_push 函数来安排的。
-
- 调用 pthread_exit 时
- 响应取消请求时
- 用非零 execute 参数调用 pthread_cleanup_pop 时
如果 execute 参数设置为 0,清理函数将不会被调用。无论哪种情况,pthread_cleanup_pop 都将删除上次 pthread_clean_push 调用建立的清理处理程序
线程睡眠、阻塞、挂起的区别与解释
首先这些术语都是对于线程来说的。对线程的控制就好比你控制了一个雇工为你干活。你对雇工的控制是通过编程来实现的。
-
- 挂起线程的意思就是你对主动对雇工说:“你睡觉去吧,用着你的时候我主动去叫你,然后接着干活”。
- 线程睡眠的意思就是你主动对雇工说:“你睡觉去吧,某时某刻过来报到,然后接着干活”。
- 线程阻塞的意思就是,你突然发现,你的雇工不知道在什么时候没经过你允许,自己睡觉呢,但是你不能怪雇工,肯定你这个雇主没注意,本来你让雇工扫地,结果扫帚被偷了或被邻居家借去了,你又没让雇工继续干别的活,他就只好睡觉了。至于扫帚回来后,雇工会不会知道,会不会继续干活,你不用担心,雇工一旦发现扫帚回来了,他就会自己去干活的。因为雇工受过良好的培训。这个培训机构就是操作系统。
例子:
线程的创建以及等待其终止

#include<iostream> #include<pthread.h> #include<unistd.h> using namespace std; void *th_fn(void *arg) { cout<<"new thread"<<endl; return (void *)10; } int main() { pthread_t ptid; void *tret; pthread_create(&ptid, NULL,th_fn ,NULL); pthread_join(ptid, &tret); cout<<"code 2 exit id = "<<(int)tret<<endl; return 0; }
运行结果:
线程的属性与限制
线程限制
线程的限制可以通过 sysconf 函数来进行查询。
-
- PTHREAD_DESTRUCTOR_ITERATIONS 线程退出时,操作系统实现试图销毁线程私有数据的最大次数。
- PTHREAD_KEYS_MAX 进程可以创建的键的最大数目
- PTHREAD_STACK_MIN 一个线程的栈可用的最小字节数
- PTHREAD_THREADS_MAX 进程可以创建的最大线程数
线程的属性
初始化线程属性/销毁线程属性
-
- pthread_attr_init(..)
- pthread_attr_destroy(..)
如果要去除对 pthread_attr_t 结构的初始化,可以调用 pthread_attr_destroy 函数。如果 pthread_attr_init 实现时为属性对象分配了动态内存空间,pthread_attr_destroy 将会释放内存空间。除此之外, pthread_attr_destroy 还会用无效的值初始化属性对象,因此如果该属性对象被误用,将会导致 pthread_create 函数返回错误。
-
- detachstate(..) 线程的分离状态属性(如果对现有的某个线程的终止状态不感兴趣的话,可以使用 pthread_detach 函数让操作系统在线程退出时收回它所占用的资源)
- guardsize(..) 线程栈末尾的警戒缓冲区大小
- stackaddr(..) 线程栈的最低地址
- stacksize(..) 线程栈的大小
设置和获得线程的分离属性
-
- pthread_attr_getdetachstate(..)
- pthread_attr_setdetachstate(..)