线程控制原语
pthread_join 函数
阻塞等待线程退出,获取线程退出状态
其作用,对应进程中的waitpid()函数
int pthread_join(pthread_t thread, void **retval);
返回值:成功:0;失败:错误号
参数:
thread:线程ID【注意】不是指针
retval:存储线程结束状态
【对比记忆】
进程中:main返回值(return xx)、exit参数均为int型值;等待子进程结束时可以用wait函数获取进程结束状态 wait函数的参数为int*
线程中:线程主函数返回值、pthread_exit的参数为void*;那么等待线程结束的pthread_join函数参数为void**
【练习】参数retval非空用法
调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方式终止,通过pthread_join得到的终止状态是不同的
(1)子线程通过pthread_exit退出
(2)子线程通过return退出
(3) 子线程返回一个自定义的结构体
【总结】
(1)如果子线程通过pthread_exit退出,那么retval所指向的单元里存放的是传给pthread_exit的参数
(2)如果子线程通过return退出,那么retval所指向的单元里存放的是子线程函数的返回值
(3)如果子线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED
(4)如果对子线程的终止状态不感兴趣,可以传NULL给retval参数
(5)一个线程创建后,最好用pthread_join回收该线程,避免产生僵尸线程
【练习】回收多个子线程
pthread_detach 函数
实现线程分离
- 线程分离状态:指定该状态,线程主动与主线程断开关系。线程结束后,其退出状态不由其他线程获取,而直接自己自动释放。网络、多线程服务器常用
- 进程若有该机制,则不会产生僵尸进程。僵尸进程的产生主要是由于进程死后,大部分资源被释放,一点残留资源仍存于系统中,导致内核认为该进程仍存在
- 也可以使用pthread_create中的参数二(线程属性)来设置线程分离
【练习】使用pthread_detach来实现线程分离
【结果分析】当主线程调用函数pthread_detach将子线程分离后,再使用pthread_join函数将子进程回收,就会报这个错(invalid argument)。由此可见,当使用pthread_detach后,便不再使用pthread_join将子线程回收,子线程会自动释放pcb
让我们将pthread_detach函数注释,再运行
【结果分析】从子线程的退出码为1可以看出pthread_join函数是运行成功的。
【总结】一般情况下,线程终止后,其终止状态一直保留到其他线程调用pthread_join获取它的状态为止,但是线程也可以被置为detach状态,这样线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态(不需要主线程调用pthread_join来回收它)。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL错误。也就是说,如果对一个线程调用了pthread_detach就不能再调用pthread_join
pthread_cancel 函数
杀死(取消)线程 其作用:对应进程中的kill()函数
int pthread_cancel(pthread_t thread); 成功:0;失败:错误号
【注意】线程的取消并不是实时的,而有一定的延时,需要等待线程到达某个取消点(检查点)
【取消点】是线程检查是否被取消,并按请求仅从动作的一个位置,通常是一些系统调用(create,open,pause,close,read,write……)凡有阻塞作有的POSIX C函数都会有取消点, (具体包括哪些,可以查看man 7 pthreads
// printf("aaa\n"); //可作取消点 凡有阻塞作有的POSIX C函数都会有取消点, (具体包括哪些,可以查看man 7 pthreads)
// pthread_testcancel(); //可作取消点 //如果屏蔽所有的取消点.主程序就会堵在pthread_join里.
// printf("bbb\n");//可作取消点
// printf("ccc\n");//可作取消点
// sleep(5);//可作取消点
// printf("ddd\n");//可作取消点
// pthread_testcancel();//可作取消点
// system("ls");//可作取消点
pthread_equal 函数
比较两个线程ID是否相等
int pthread_equal(pthread_t t1, pthread_t t2);
- 目前没什么用,可以直接用 == 判断
- 有可能Linux在未来线程ID pthread_t类型被改为结构体实现
控制原语对比
进程 | 线程 | |
---|---|---|
创建 | fork() | pthread_create |
退出 | exit(int) | pthread_exit(void*) |
等待 | wait(int*) | pthread_join(thread, void**) 阻塞等待 |
杀死 | kill | pthread_cancel 取消点(man 7 pthreads) |
获取ID | getpid | pthread_self() |
分离 | 无 | pthread_detach() 自动清理pcb |