《unix高级环境编程》线程——线程终止

pthread_exit 函数和 pthread_join 函数

        在进程中,若调用了函数 exit_exit,或_Exit 时,则该进程会终止,同样,若进程中的线程调用这三个函数时也会使线程所在的进程终止。那么要是只是退出线程,而不终止线程所在的进程有什么办法?下面是在单线程模式下退出线程的三种方式(不会终止线程所在的进程):

  1. 线程只是从启动例程中返回,返回值是线程的退出码;
  2. 线程被同一进程的其他线程取消;
  3. 线程调用 pthread_exit 函数;
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 线程终止 */  
  2.   
  3. /* 
  4.  * 函数功能:退出线程; 
  5.  * 无返回值; 
  6.  * 函数原型: 
  7.  */  
  8. #include <pthread.h>  
  9. void pthread_exit(void *rval_ptr);  
  10. /* 
  11.  * 说明: 
  12.  * rval_ptr是一个无类型指针,与传给启动例程的单个参数类似; 
  13.  * 进程中的其他线程可以通过调用pthread_join函数访问到这个指针; 
  14.  */  
  15.   
  16. /* 
  17.  * 函数功能:获取其他已终止的线程的退出码; 
  18.  * 返回值:若成功则返回0,否则返回错误编码; 
  19.  * 函数原型: 
  20.  */  
  21. int pthread_join(pthread_t threadvoid **rval_ptr);  
  22. /* 
  23.  * 说明: 
  24.  * 调用pthread_join的线程将一直阻塞,直到thread指定的线程调用pthread_exit、从启动例程返回或被取消; 
  25.  * 如果线程只是从启动例程返回,rval_ptr将包含返回码; 
  26.  * 如果线程是被取消,由rval_ptr指向的内存单元设置为PTHREAD_CANCELED; 
  27.  */  
        通常父进程需要调用 wait 函数族等待子进程避免子进程成为僵尸进程。在线程中为确保终止线程的资源对进程可用,即回收终止线程的资源,应该在每个线程结束时分离它们。一个没有被分离的线程终止时会保留其虚拟内存,包括它们的堆栈和其他系统资源。分离线程意味着通知系统不再需要此线程,允许系统将分配给它的资源回收。

        调用  pthread_join 函数将自动分离指定的线程,被分离的线程就再也不能被其他线程连接了,即恢复了系统资源。若线程已处于分离状态,调用 pthread_join 会失败,将返回 EINVAL。所以,如果多个线程需要知道某个特定的线程何时结束,则这些线程应该等待某个条件变量而不是调用 pthread_join
       下面针对这两个函数进行测试:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include "apue.h"  
  2. #include <pthread.h>  
  3.   
  4. static void *func1(void *arg);  
  5. static void *func2(void *arg);  
  6.   
  7. int main(void)  
  8. {  
  9.     pthread_t tid1, tid2;  
  10.     int err;  
  11.     void *tret;  
  12.   
  13.     err = pthread_create(&tid1, NULL, func1, NULL);  
  14.     if(err != 0)  
  15.         err_quit("can't create thread 1: %s\n", strerror(err));  
  16.     err = pthread_create(&tid2, NULL, func2, NULL);  
  17.     if(err != 0)  
  18.         err_quit("can't create thread 2: %s\n", strerror(err));  
  19.   
  20.     err = pthread_join(tid1,&tret);  
  21.     if(err != 0)  
  22.         err_quit("can't join with thread 1: %s\n", strerror(err));  
  23.     printf("thread 1 exit code %d\n", (int)tret);  
  24.   
  25.     err = pthread_join(tid2,&tret);  
  26.     if(err != 0)  
  27.         err_quit("can't join with thread 2: %s\n", strerror(err));  
  28.     printf("thread 2 exit code %d\n", (int)tret);  
  29.   
  30.     exit(0);  
  31. }  
  32.   
  33. static void *func1(void *arg)  
  34. {  
  35.     printf("thread 1 returning\n");  
  36.     return((void *)1);  
  37. }  
  38.   
  39. static void *func2(void *arg)  
  40. {  
  41.     printf("thread 2 exiting\n");  
  42.     pthread_exit((void*)2);  
  43. }  

输出结果:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. thread 1 returning  
  2. thread 2 exiting  
  3. thread 1 exit code 1  
  4. thread 2 exit code 2  

pthread_cancel 函数

          在同一进程中,线程可以请求取消同一进程的其他线程, pthread_cancel 函数可以实现该功能。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 函数功能:请求取消同一进程的其他线程; 
  3.  * 返回值:若成功则返回0,否则返回错误; 
  4.  * 函数原型: 
  5.  */  
  6. int pthread_cancel(pthread_t tid);  
  7. /* 
  8.  * 说明: 
  9.  * 在默认情况下,该函数会使得由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit函数, 
  10.  * 但是线程可以选择忽略取消方式或控制取消方式; 
  11.  * 注意:pthread_cancel并不等待线程终止,它仅仅提出请求; 
  12.  */  

 pthread_cleanup_push 函数和 pthread_cleanup_pop 函数

       在进程中可以调用  atexit 函数注册在  main 结束后调用清理程序的函数。同样,线程也可以安排它退出时需要调用的清理处理程序函数。可以把每个线程考虑为有一个活动的清理处理程序函数的栈。调用 pthread_cleanup_push 将清理处理程序函数加到栈中,调用 pthread_cleanup_pop 删除最近添加的清理处理程序函数。当线程退出时,从最近添加的清理处理程序函数开始,各个活动的清理处理程序函数都将被调用,当所有清理处理程序函数返回时,线程被终止。当 pthread_cleanup_pop 中的参数  execute 为非 0 时,最近添加的清理处理程序函数也将被调用,然后删除。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 函数功能:线程清理处理程序; 
  3.  * 无返回值; 
  4.  * 函数原型: 
  5.  */  
  6. void pthread_cleanup_push(void(*rtn)(void *), void *arg);  
  7. void pthread_cleanup_pop(int execute);  
  8. /* 
  9.  * 说明: 
  10.  * 当线程执行以下动作时调用清理函数,调用参数为arg: 
  11.  * (1)调用pthread_exit函数时; 
  12.  * (2)响应取消请求时; 
  13.  * (3)用非零execute参数调用pthread_cleanup_pop时; 
  14.  * 如果参数execute设置为0,清理程序函数不被调用; 
  15.  */  
测试程序:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <pthread.h>  
  2. #include <sys/types.h>  
  3. #include "apue.h"  
  4.   
  5. static void cleanup(void *arg);  //线程清理函数  
  6. void *func1(void *arg);  
  7. void *func2(void *arg);  
  8.   
  9. int main(void)  
  10. {  
  11.     pthread_t tid1;  
  12.     pthread_t tid2;  
  13.     int       err;  
  14.     void   *tret;  
  15.   
  16.     err = pthread_create(&tid1, NULL, func1, (void*)1);  
  17.     if(err != 0)  
  18.         err_quit("can't create thread 1: %s\n", strerror(err));  
  19.     err = pthread_create(&tid2, NULL, func2, (void*)1);  
  20.     if(err != 0)  
  21.         err_quit("can't create thread 2: %s\n", strerror(err));  
  22.   
  23.     err = pthread_join(tid1,&tret);  
  24.     if(err != 0)  
  25.         err_quit("can't join with thread 1: %s\n", strerror(err));  
  26.     printf("thread 1 exit code %d\n", (int)tret);  
  27.     err = pthread_join(tid2, &tret);  
  28.     if(err != 0)  
  29.         err_quit("can't join with thread 2: %s\n", strerror(err));  
  30.     printf("thread 2 exit code %d\n",(int)tret);  
  31.     exit(0);  
  32. }  
  33.   
  34. static void cleanup(void *arg)  
  35. {  
  36.     printf("cleanup: %s\n", (char*)arg);  
  37. }  
  38. void *func1(void *arg)  
  39. {  
  40.    printf("thread 1 start.\n");  
  41.    pthread_cleanup_push(cleanup,"thread 1 first handler");  
  42.    pthread_cleanup_push(cleanup,"thread 1 second handler");  
  43.    printf("thread 1 push complete.\n");  
  44.    if(arg)  
  45.     return ((void*)1);   //返回终止,将不会调用清理处理程序  
  46.    pthread_cleanup_pop(0);  
  47.    pthread_cleanup_pop(0);  
  48.    return ((void*)1);  
  49. }  
  50. void *func2(void *arg)  
  51. {  
  52.    printf("thread 2 start.\n");  
  53.    pthread_cleanup_push(cleanup,"thread 2 first handler");  
  54.    pthread_cleanup_push(cleanup,"thread 2 second handler");  
  55.    printf("thread 2 push complete.\n");  
  56.    if(arg)  
  57.      pthread_exit((void*)2); //会调用清理处理程序  
  58.    pthread_cleanup_pop(0);  
  59.    pthread_cleanup_pop(0);  
  60.    pthread_exit((void*)2);  
  61. }  
输出结果:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. thread 1 start.  
  2. thread 2 start.  
  3. thread 2 push complete.  
  4. cleanup: thread 2 second handler  
  5. cleanup: thread 2 first handler  
  6. thread 1 push complete.  
  7. thread 1 exit code 1  
  8. thread 2 exit code 2  

pthread_detach 函数

        在默认情况下,线程终止状态会保存到对该线程调用 pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即被收回。当线程被分离时,并不能用 pthread_join 函数等待它的终止状态,对分离状态的线程进行 pthread_join 的调用会产生失败,返回 EINVAL。pthread_detach 调用可以用于线程进入分离状态。如果不想等待创建的某个线程,而且知道不再需要控制它,就可以调用 pthread_detach 来分离它。线程可以分离自己,任何知道其ID的其他线程也可以随时分离它。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 函数功能:使线程处于分离状态; 
  3.  * 返回值:若成功则返回0,否则返回错误编码; 
  4.  * 函数原型: 
  5.  */  
  6. int pthread_detach(pthread_t tid);  
测试程序:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <pthread.h>  
  2. #include "apue.h"  
  3. void *thr_fn(void *arg)  
  4. {  
  5.         printf("thread start ...\n");  
  6.         printf("thread exiting...\n");  
  7.         pthread_exit((void *)1);  
  8. }  
  9.   
  10. int main(void)  
  11. {  
  12.         pthread_t tid1;  
  13.         int err;  
  14.         void* tret;  
  15.   
  16.         err = pthread_create(&tid1, NULL, thr_fn, (void*)1);  
  17.         if(err != 0)  
  18.             err_quit("pthread_create error: %s\n", strerror(err));  
  19.   
  20.         err = pthread_detach(tid1);  
  21.         if(err != 0)  
  22.             err_quit("pthread_detach error: %s\n", strerror(err));  
  23.   
  24.         err = pthread_join(tid1,&tret);  
  25.         if(err != 0)  
  26.             err_quit("pthread_join error: %s\n", strerror(err));  
  27.         printf("thread 1 exit code:%d\n",(int)tret);  
  28.         exit(0);  
  29. }  
输出结果:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. thread start ...  
  2. thread exiting...  
  3. pthread_join error: Invalid argument  
因为已经使用 pthread_detach 对线程进行分离,第二次调用 pthread_join 对线程进行分离时会返回 EINVAL。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值