linux高级编程之线程间的通信(pthread_cleanup_push和pthread_cleanup_pop)
版权声明:转载请注明出处: https://blog.csdn.net/longbei9029/article/details/72871714
linux高级编程之线程间的通信(pthread_cleanup_push和pthread_cleanup_pop)
线程可以安排他退出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的。这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说他们的执行顺序与他们注册的顺序相反。
pthread_cleanup_push和pthread_cleanup_pop函数原型如下:
头文件:#include <pthread.h>
函数原型:void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_clean_pop(int execute);
void(*rtn)(void *): 线程清理函数
另外简单记录下pthread_cancel函数。该函数为线程取消函数,用来取消同一进程中的其他进程,函数原型:
头文件: #include <pthread.h>
函数原型:pthread_cancel(pthread_t tid);
tid: 线程id
当线程执行以下动作时,调用清理函数,调用的参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的。
●调用pthread_exit时
●响应取消请求时
●用非零execute参数调用pthread_cleanup_pop时
关于书上有句原话:“如果execute参数置为0,清理函数将不被调用”,我觉得说的有问题,而且接下来我摘抄了书上的一个例子,刚好验证了他说的这句话的错误,而且我也验证了下,当然在一篇博客中我看到这样的解释觉得很合理:当pthread_cleanup_pop()函数的参数为0时,仅仅在线程调用pthread_exit函数或者其它线程对本线程调用 pthread_cancel函数时,才在弹出“清理函数”的同时执行该“清理函数”。同样我也取了博客中的第二个例子来说明pthread_cancel调用时,pthread_cleanup_push会用清理函数。(ps:重新看了下这个疑问,翻书看了下,其实后面还有句话:“不管上述哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理函数”,所以可能作者想表达的是设置为pop参数设置为0,在其它两种情况下同样会被调用)
参考博客:http://blog.chinaunix.net/uid-26772137-id-3369725.html
例1:
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <pthread.h>
-
void cleanup(void *arg)
-
{
-
printf("cleanup:%s\n",(char*)arg);
-
}
-
void *thr_fn1(void *arg)
-
{
-
printf("thread 1 start\n");
-
pthread_cleanup_push(cleanup,"thread 1 first handler");
-
pthread_cleanup_push(cleanup,"thread 1 second handler");
-
printf("thread 1 push complete\n");
-
if(arg)
-
return ((void *)1);
-
pthread_cleanup_pop(0);
-
pthread_cleanup_pop(0);
-
return ((void *)1);
-
}
-
void *thr_fn2(void *arg)
-
{
-
printf("thread 2 start\n");
-
pthread_cleanup_push(cleanup,"thread 2 first handler");
-
pthread_cleanup_push(cleanup,"thread 2 second handler");
-
printf("thread 2 push complete\n");
-
if(arg)
-
pthread_exit((void *)2);
-
pthread_cleanup_pop(0);
-
pthread_cleanup_pop(0);
-
pthread_exit((void *)2);
-
}
-
int main()
-
{
-
int err;
-
pthread_t tid1,tid2;
-
void *tret;
-
err = pthread_create(&tid1,NULL,thr_fn1,(void *)1);
-
if(err != 0)
-
{
-
fprintf(stderr,"thread create 1 is error\n");
-
return -1;
-
}
-
err = pthread_create(&tid2,NULL,thr_fn2,(void *)1);
-
if(err != 0)
-
{
-
fprintf(stderr,"thread create 2 is error\n");
-
return -2;
-
}
-
err = pthread_join(tid1,&tret);
-
if(err != 0)
-
{
-
fprintf(stderr,"can't join with thread 1\n");
-
return -2;
-
}
-
//pthread_cancel(tid1);
-
printf("thread 1 exit code %d\n",tret);
-
err = pthread_join(tid2,&tret);
-
if(err != 0)
-
{
-
fprintf(stderr,"can't join with thread 2\n");
-
return -2;
-
}
-
printf("thread 2 exit code %d\n",tret);
-
return 0;
-
}
运行结果如下:
从输出结果可以看出:两个线程都调用了,但是却只调用了第二个线程的清理处理程序,所以如果线程是通过从它的启动历程中返回而终止的话,那么它的清理处理程序就不会被调用,还要注意清理程序是按照与它们安装时相反的顺序被调用的。从代码输出也可以看到先执行的thread 2 second handler后执行的thread 2 first handler。
例2:
-
#include<stdlib.h>
-
#include<stdio.h>
-
#include<unistd.h>
-
#include<pthread.h>
-
void clean_fun1(void * arg)
-
{
-
printf("this is clean fun1\n");
-
}
-
void clean_fun2(void * arg)
-
{
-
printf("this is clean fun2\n");
-
}
-
void * thread_fun(void * arg)
-
{
-
pthread_cleanup_push(clean_fun1,NULL);
-
pthread_cleanup_push(clean_fun2,NULL);
-
sleep(100);
-
//这里要注意,如果将sleep(100);换成while(1);的话,程序会一直暂停.push和pop要成对出现.
-
//因为while(1);运行的太快,线程不接受cancel信号
-
//while(1);
-
pthread_cleanup_pop(0);
-
pthread_cleanup_pop(0);
-
return NULL;
-
}
-
int main()
-
{
-
pthread_t tid1;
-
int err;
-
err=pthread_create(&tid1,NULL,thread_fun,NULL);
-
if(err!=0)
-
{
-
perror("pthread_create");
-
exit(0);
-
}
-
sleep(3);
-
//printf("test\n");
-
err=pthread_cancel(tid1);
-
if(err!=0)
-
{
-
perror("cancel error:");
-
exit(0);
-
}
-
err=pthread_join(tid1,NULL);
-
if(err!=0)
-
{
-
perror("pthread_join error:");
-
exit(0);
-
}
-
return 0;
-
}
运行结果如下:
从上面也可以看出,当调用pthread_cancel函数请求后,等到响应请求时,代码调用了pthread_clean_push函数中的clean_fun1和clean_fun2,函数clean_fun2中的语句先被打印。
====================================================================
=====================================================
linux高级编程之线程间的通信(pthread_cleanup_push和pthread_cleanup_pop)
线程可以安排他退出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的。这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说他们的执行顺序与他们注册的顺序相反。
pthread_cleanup_push和pthread_cleanup_pop函数原型如下:
头文件:#include <pthread.h>
函数原型:void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_clean_pop(int execute);
void(*rtn)(void *): 线程清理函数
另外简单记录下pthread_cancel函数。该函数为线程取消函数,用来取消同一进程中的其他进程,函数原型:
头文件: #include <pthread.h>
函数原型:pthread_cancel(pthread_t tid);
tid: 线程id
当线程执行以下动作时,调用清理函数,调用的参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的。
●调用pthread_exit时
●响应取消请求时
●用非零execute参数调用pthread_cleanup_pop时
关于书上有句原话:“如果execute参数置为0,清理函数将不被调用”,我觉得说的有问题,而且接下来我摘抄了书上的一个例子,刚好验证了他说的这句话的错误,而且我也验证了下,当然在一篇博客中我看到这样的解释觉得很合理:当pthread_cleanup_pop()函数的参数为0时,仅仅在线程调用pthread_exit函数或者其它线程对本线程调用 pthread_cancel函数时,才在弹出“清理函数”的同时执行该“清理函数”。同样我也取了博客中的第二个例子来说明pthread_cancel调用时,pthread_cleanup_push会用清理函数。(ps:重新看了下这个疑问,翻书看了下,其实后面还有句话:“不管上述哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理函数”,所以可能作者想表达的是设置为pop参数设置为0,在其它两种情况下同样会被调用)
参考博客:http://blog.chinaunix.net/uid-26772137-id-3369725.html
例1:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void cleanup(void *arg)
{
printf("cleanup:%s\n",(char*)arg);
}
void *thr_fn1(void *arg)
{
printf("thread 1 start\n");
pthread_cleanup_push(cleanup,"thread 1 first handler");
pthread_cleanup_push(cleanup,"thread 1 second handler");
printf("thread 1 push complete\n");
if(arg)
return ((void *)1);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return ((void *)1);
}
void *thr_fn2(void *arg)
{
printf("thread 2 start\n");
pthread_cleanup_push(cleanup,"thread 2 first handler");
pthread_cleanup_push(cleanup,"thread 2 second handler");
printf("thread 2 push complete\n");
if(arg)
pthread_exit((void *)2);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
pthread_exit((void *)2);
}
int main()
{
int err;
pthread_t tid1,tid2;
void *tret;
err = pthread_create(&tid1,NULL,thr_fn1,(void *)1);
if(err != 0)
{
fprintf(stderr,"thread create 1 is error\n");
return -1;
}
err = pthread_create(&tid2,NULL,thr_fn2,(void *)1);
if(err != 0)
{
fprintf(stderr,"thread create 2 is error\n");
return -2;
}
err = pthread_join(tid1,&tret);
if(err != 0)
{
fprintf(stderr,"can't join with thread 1\n");
return -2;
}
//pthread_cancel(tid1);
printf("thread 1 exit code %d\n",tret);
err = pthread_join(tid2,&tret);
if(err != 0)
{
fprintf(stderr,"can't join with thread 2\n");
return -2;
}
printf("thread 2 exit code %d\n",tret);
return 0;
}
运行结果如下:
从输出结果可以看出:两个线程都调用了,但是却只调用了第二个线程的清理处理程序,所以如果线程是通过从它的启动历程中返回而终止的话,那么它的清理处理程序就不会被调用,还要注意清理程序是按照与它们安装时相反的顺序被调用的。从代码输出也可以看到先执行的thread 2 second handler后执行的thread 2 first handler。
例2:
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void clean_fun1(void * arg)
{
printf("this is clean fun1\n");
}
void clean_fun2(void * arg)
{
printf("this is clean fun2\n");
}
void * thread_fun(void * arg)
{
pthread_cleanup_push(clean_fun1,NULL);
pthread_cleanup_push(clean_fun2,NULL);
sleep(100);
//这里要注意,如果将sleep(100);换成while(1);的话,程序会一直暂停.push和pop要成对出现.
//因为while(1);运行的太快,线程不接受cancel信号
//while(1);
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return NULL;
}
int main()
{
pthread_t tid1;
int err;
err=pthread_create(&tid1,NULL,thread_fun,NULL);
if(err!=0)
{
perror("pthread_create");
exit(0);
}
sleep(3);
//printf("test\n");
err=pthread_cancel(tid1);
if(err!=0)
{
perror("cancel error:");
exit(0);
}
err=pthread_join(tid1,NULL);
if(err!=0)
{
perror("pthread_join error:");
exit(0);
}
return 0;
}
运行结果如下:
从上面也可以看出,当调用pthread_cancel函数请求后,等到响应请求时,代码调用了pthread_clean_push函数中的clean_fun1和clean_fun2,函数clean_fun2中的语句先被打印。
=====================================================
=====================================================================
=======================================================
UNIX(编程-线程基础):05---线程退出清理函数(pthread_cleanup_push、pthread_cleanup_pop)
2019年04月15日 10:59:07 江南、董少 阅读数 115
一、函数介绍
#include <pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);
函数功能:
- pthread_cleanup_push:向栈中注册一个线程退出清理函数。因为处理程序记录在栈中,所以先注册的pthread_cleanup_push函数后执行。(并且pthread_cleanup_push函数可以设置多个)
- pthread_cleanup_pop:如果参数为0,就删除一个栈顶注册的线程退出清理函数。(注意:调用一次只清理一个)
pthread_cleanup_push函数:
当线程执行下列动作时,pthread_cleanup_push函数就会执行参数1所指向的函数,参数1所指的函数的参数来自于参数2arg
- ①调用 pthread_exit 时
- ②响应取消请求时
- ③用非零execute参数调用pthread_cleanup_pop函数时
重点:如果不是上面3中情况(例如在线程中return等),pthread_cleanup_push函数就不会执行
pthread_cleanup_pop函数:
- 如果参数为0,用来删除最新一次向栈内注册的线程退出清理函数
这两个函数的限制:
- 由于他们可以实现为宏,所以必须在与线程相同的作用域中以匹配对的形式使用。例如:pthread_cleanup_push的宏可以包含字符“{”,这种情况下,在 pthread_cleanup_pop 的定义中要有对应的匹配字符“}”
二、演示案例
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void cleanup(void *arg){
printf("cleanup: %s\n", (char *)arg);
}
void *thr_fn1(void *arg){
printf("thread 1 start...\n");
pthread_cleanup_push(cleanup, "thread 1 first handler");
pthread_cleanup_push(cleanup, "thread 1 second handler");
printf("thread 1 push complete\n");
if (arg) {
return ((void*)1);
}
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return ((void *)1);
}
void *thr_fn2(void *arg){
printf("thread 2 start...\n");
pthread_cleanup_push(cleanup, "thread 2 first handler");
pthread_cleanup_push(cleanup, "thread 2 second handler");
printf("thread 2 push complete\n");
if (arg) {
pthread_exit((void*)2);
}
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
pthread_exit((void*)2);
}
int main(void)
{
int err;
pthread_t tid1, tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);//创建线程1
if (err != 0) {
printf("Can't create thread 1\n");
exit(0);
}
err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);//创建线程2
if (err != 0) {
printf("Can't create thread 2\n");
exit(0);
}
err = pthread_join(tid1, &tret);//等到线程1
if (err != 0) {
printf("Can't join with thread 1\n");
exit(0);
}
printf("thread 1 exit code: %ld\n", (long)tret);
err = pthread_join(tid2, &tret);//等待线程2
if (err != 0) {
printf("Can't join with thread 2\n");
exit(0);
}
printf("thread 2 exit code: %ld\n", (long)tret);
exit(0);
}
运行结果
=======================================================
=======================================================================
===========================================================
Linux 下子线程的 pthread_cleanup_push() 和 pthread_cleanup_pop() 研究
线程退出前可能有一些清理工作,但是这部分代码又不会放到线程主体部分,就需要挂接一个或者几个线程“清洁工”来做这部分事情。需要这对兄弟:
#include<pthread.h>
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);
显然pthread_cleanup_push() 是挂接 清理函数的,它的返回值类型为 void,有两个入口参数,第一个参数是清理函数函数指针,第二个参数是传给清理函数的 typeless pointer 。
另一个兄弟 pthread_cleanup_pop() 是来触发清理函数的,是按照相反的顺序来触发清理函数的。而如果它的入口参数 execute 为0值,则对应的清理函数并没有真正的执行。
例如下面这个例子:
1 /****************************************************************
2 # File Name: thread_cleanup3.c
3 # Author : lintex9527
4 # E-Mail : lintex9527@yeah.net
5 # Created Time: Sat 22 Aug 2015 03:25:09 PM HKT
6 # Purpose : 测试清理函数的触发顺序,以及执行与否。
7 # Outline :
8 # Usage :
9 # --------------------------------------------------
10 # Result :
11 # --------------------------------------------------
12 *****************************************************************/
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <pthread.h>
16
17 /* 线程传递给 清理函数 的参数结构体 */
18 struct argtype{
19 int a,b;
20 int result;
21 };
22
23 void print_argtype(const char *str, struct argtype *p)
24 {
25 printf("%s\n", str);
26 printf(" a = %d, b = %d\n", p->a, p->b);
27 printf(" result = %d\n", p->result);
28 }
29
30 /* for thread 1 */
31 struct argtype entity1 = {
32 .a = 50,
33 .b = 5,
34 .result = 11
35 };
36
37 /* 以下是3个清理函数 */
38 void cleanup_add(void *arg)
39 {
40 struct argtype *p = (struct argtype *)arg;
41 p->result = p->a + p->b;
42 print_argtype("cleanup [add]", p);
43 //pthread_exit((void *)p->result);
44 }
45
46 void cleanup_minus(void *arg)
47 {
48 struct argtype *p = (struct argtype *)arg;
49 p->result = p->a - p->b;
50 print_argtype("cleanup [minus]", p);
51 //pthread_exit((void *)p->result);
52 }
53
54
55 void cleanup_times(void *arg)
56 {
57 struct argtype *p = (struct argtype *)arg;
58 p->result = p->a * p->b;
59 print_argtype("cleanup [times]", p);
60 //pthread_exit((void *)p->result);
61 }
62
63 /* 子线程1函数,临时地改变了entity1结构体中成员值,检查清理函数执行点 */
64 void* thr1_fun(void *arg)
65 {
66 printf("Now thread1 [%lu] start:\n", pthread_self());
67
68 pthread_cleanup_push(cleanup_times, (void *)&entity1); // cleanup_times
69 entity1.a = 20;
70 entity1.b = 2;
71 pthread_cleanup_push(cleanup_minus, (void *)&entity1); // cleanup_minus
72 pthread_cleanup_push(cleanup_add, (void *)&entity1); // cleanup_add
73 pthread_cleanup_pop(3); // cleanup_add
74
75 entity1.a = 30;
76 entity1.b = 3;
77 pthread_cleanup_pop(1); // cleanup_minus
78
79 entity1.a = 40;
80 entity1.b = 4;
81 pthread_cleanup_pop(1); // cleanup_times
82
83 entity1.a = 80;
84 entity1.b = 8;
85 pthread_exit((void *)entity1.result);
86 }
87
88
89 int main(void)
90 {
91 int err;
92 pthread_t tid1;
93 void *tret;
94
95 err = pthread_create(&tid1, NULL, thr1_fun, NULL);
96 err = pthread_join(tid1, &tret);
97 if (err != 0)
98 {
99 perror("pthread_join");
100 return -1;
101 }
102
103 printf("In main get result [%d] from thread %lu\n", tret, tid1);
104 print_argtype("main:", &entity1);
105
106 return 0;
107 }
执行结果:
$ ./thread_cleanup3.exe
Now thread1 [140090204903168] start:
cleanup [add]
a = 20, b = 2
result = 22
cleanup [minus]
a = 30, b = 3
result = 27
cleanup [times]
a = 40, b = 4
result = 160
In main get result [160] from thread 140090204903168
main:
a = 80, b = 8
result = 160
顺序测试
在这个例子中,我把 pthread_cleanup_pop(int execute) 中的 execute 都设定为非零值,测试3个清理函数的调用顺序,
注册的顺序是: cleanup_times --> cleanup_minus --> cleanup_add
调用的顺序是: cleanup_add --> cleanup_minus --> cleanup_times
的的确确是按照相反的顺序调用的。
执行点测试
为了测试每一个清理函数的执行点,我在每一个pthread_cleanup_pop() 之前都修改了 结构体 entity1 的域 a,b。经过比对发现每一个 pthread_cleanup_push() 和 pthread_cleanup_pop() 形成一个 pairs,因为它们是基于宏实现的,pthread_cleanup_push() 中包含了一个“{”,而 pthread_cleanup_pop() 中包含了一个“}” 和前面的对应,因此它们必须成对的出现,否则代码通不过编译。经过检查和对比,发现每一个 pairs 虽然在代码形式上互相嵌套,但是它们的执行没有互相嵌套。即在执行最外面的 cleanup_times() 并没有递归调用 cleanup_minus() 继而递归调用 cleanup_times()。
因此在处理最外面的 cleanup_times() 时屏蔽了从 pthread_cleanup_push(cleanup_minus, xxx) 到 pthread_cleanupo_pop(yyy) (与 cleanup_minus 对应的) 部分的代码。
而在处理 cleanup_minus() 时屏蔽了从 pthread_cleanup_push(cleanup_add, xxx) 到 pthread_cleanup_pop(yyy) (与 cleanup_add 对应的) 部分的代码。
因为 pop 顺序和 push 顺序是相反的,那么从第一个 pop 的顺序开始执行: cleanup_add --> cleanup_minus --> cleanup_times.
但是每一次执行 cleanup_xxx 的参数为什么会不一样的呢?是从哪里开始变化的呢?
是从线程函数入口上到下,一直到 pthread_cleanup_pop() 部分的参数对当前的 cleanup_xxx() 函数有效。在当前 pthread_cleanup_pop() 下面的语句是对后面一个 pop() 函数起作用的。
如下面这张图:
左边的指示线条表征的是每一个 push 入栈的清理函数可访问的资源区;
右边的双箭头线表征的是 push / pop 对子,虽然在代码形式上有嵌套,但是在函数执行上并不会嵌套执行。
根据分析,
entity1.a , entity1.b 传递给 cleanup_add() 函数的值是 20 , 2;
entity1.a , entity1.b 传递给 cleanup_minus() 函数的值是 30, 3;
entity1.a , entity1.b 传递给 cleanup_times() 函数的值是 40, 4;
而最终在 main thread 中可以访问到的 entity1.a, entity1.b 的值是 80 , 8 。那个时候已经没有 清理函数 cleanup_xxx() 去访问 entity1 结构体了。
另外,我原本在清理函数内部添加了 pthread_exit() 函数,这会出现什么情况呢?比如取消 cleanup_times() 函数里 pthread_exit() 之前的注释,编译运行结果如下:
$ ./thread_cleanup3.exe
Now thread1 [140415830189824] start:
now cleanup_add.
cleanup [add]
a = 20, b = 2
result = 22
now cleanup_minus.
cleanup [minus]
a = 30, b = 3
result = 27
now cleanup_times.
cleanup [times]
a = 40, b = 4
result = 160
In main get result [160] from thread 140415830189824
main:
a = 40, b = 4
result = 160
对比之前,发现在 main thread 中的 a,b 值是40, 4 ,这和子线程退出点有关,子线程没有走到下面这一步:
entity1.a = 40;
entity1.b = 4;
printf("now cleanup_times.\n");
pthread_cleanup_pop(1); // cleanup_times
-------------------------------------------------------------------// 下面没有执行
entity1.a = 80;
entity1.b = 8;
printf("thread 1 is exit...\n");
pthread_exit((void *)entity1.result);
说明提前使用 pthread_exit() 那么各个函数访问的资源就更受限。
但是在2个及以上的清理函数中添加 pthread_exit() ,会导致线程不断地调用 清理函数,进入死机状态。
总结就是不要在清理函数中添加 pthread_exit() 。
===========================================================
=======================================================================
=============================================================
pthread_join和pthread_detach的用法(转)
一:关于join
join
join是三种同步线程的方式之一。另外两种分别是互斥锁(mutex)和条件变量(condition variable)。
调用pthread_join()将阻塞自己,一直到要等待加入的线程运行结束。
可以用pthread_join()获取线程的返回值。
一个线程对应一个pthread_join()调用,对同一个线程进行多次pthread_join()调用是逻辑错误。
join or detach
线程分两种:一种可以join,另一种不可以。该属性在创建线程的时候指定。
joinable线程可在创建后,用pthread_detach()显式地分离。但分离后不可以再合并。该操作不可逆。
为了确保移植性,在创建线程时,最好显式指定其join或detach属性。似乎不是所有POSIX实现都是用joinable作默认。
二: pthread_detach
创建一个线程默认的状态是joinable, 如果一个线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收(退出状态码),所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源(类似于wait,waitpid)
但是调用pthread_join(pthread_id)后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此,比如在Web服务器中当主线程为每个新来的链接创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的链接),
这时可以在子线程中加入代码
pthread_detach(pthread_self())
或者父线程调用
pthread_detach(thread_id)(非阻塞,可立即返回)
这将该子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。
三:pthread_join
调用pthread_join的线程会阻塞,直到指定的线程返回,调用了pthread_exit,或者被取消。
如果线程简单的返回,那么rval_ptr被设置成线程的返回值,参见范例1;如果调用了pthread_exit,则可将一个无类型指针返回,在pthread_join中对其进行访问,参见范例2;如果线程被取消,rval_ptr被设置成PTHREAD_CANCELED。
如果我们不关心线程的返回值,那么我们可以把rval_ptr设置为NULL。
范例1:
#include <pthread.h>
#include <string.h>
void *thr_fn1(void *arg)
{
printf(“thread 1 returning.\n”);
return((void *)1);
}
void *thr_fn2(void *arg)
{
printf(“thread 2 exiting.\n”);
return((void *)2);
}
int main()
{
pthread_t tid1,tid2;
void *tret;
pthread_create(&tid1,NULL,thr_fn1,NULL);
pthread_create(&tid2,NULL,thr_fn2,NULL);
pthread_join(tid1,&tret);
printf(“thread 1 exit code %d\n”,(int)tret);
pthread_join(tid2,&tret);
printf(“thread 2 exit code %d\n”,(int)tret);
exit(0);
}
运行结果:
thread 1 returning.
thread 1 exit code 1.
thread 2 exiting.
thread 2 exit code 2.
范例2:
#include <stdio.h>
#include <pthread.h>
void thread1(char s[])
{
printf("This is a pthread1.\n");
printf("%s\n",s);
pthread_exit("Hello first!"); //结束线程,返回一个值。
}
void thread2(char s[])
{
printf("This is a pthread2.\n");
printf("%s\n",s);
pthread_exit("Hello second!");
}
int main(void)
{
pthread_t id1,id2;
void *a1,*a2;
int i,ret1,ret2;
char s1[]="This is first thread!";
char s2[]="This is second thread!";
ret1=pthread_create(&id1,NULL,(void *) thread1,s1);
ret2=pthread_create(&id2,NULL,(void *) thread2,s2);
if(ret1!=0){
printf ("Create pthread1 error!\n");
exit (1);
}
pthread_join(id1,&a1);
printf("%s\n",(char*)a1);
if(ret2!=0){
printf ("Create pthread2 error!\n");
exit (1);
}
printf("This is the main process.\n");
pthread_join(id2,&a2);
printf("%s\n",(char*)a2);
return (0);
}
运行结果:
[****@XD**** c]$ ./example
This is a pthread1.
This is first thread!
Hello first!
This is the main process.
This is a pthread2.
<参考资料语>
一般情况下,进程中各个线程的运行都是相互独立的,线程的终止并不会通知,也不会影响其他线程,终止的线程所占用的资源也并不会随着线程的终止而得到释 放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数
pthread_join()的调用者将挂起并等待th线程终止,retval是pthread_exit()调用者线程(线程ID为th)的返回值,如 果thread_return不为NULL,则*thread_return=retval。需要注意的是一个线程仅允许唯一的一个线程使用 pthread_join()等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态
如果进程中的某个线程执行了pthread_detach(th),则th线程将处于DETACHED状态,这使得th线程在结束运行时自行释放所占用的 内存资源,同时也无法由pthread_join()同步,pthread_detach()执行之后,对th请求pthread_join()将返回错误
一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止,要么已设为DETACHED,要么就需要使用pthread_join()来回收
3) 主线程用pthread_exit还是return
用pthread_exit只会使主线程自身退出,产生的子线程继续执行;用return则所有线程退出。
综合以上要想让子线程总能完整执行(不会中途退出),一种方法是在主线程中调用pthread_join对其等待,即pthread_create/pthread_join/pthread_exit或return;一种方法是在主线程退出时使用pthread_exit,这样子线程能继续执行,即pthread_create/pthread_detach/pthread_exit;还有一种是pthread_create/pthread_detach/return,这时就要保证主线程不能退出,至少是子线程完成前不能退出。现在的项目中用的就是第三种方法,主线程是一个死循环,子线程有的是死循环有的不是。
<参考资料语>
理论上说,pthread_exit()和线程宿体函数退出的功能是相同的,函数结束时会在内部自动调用pthread_exit()来清理线程相关的资源。但实际上二者由于编译器的处理有很大的不同。
在进程主函数(main())中调用pthread_exit(),只会使主函数所在的线程(可以说是进程的主线程)退出;而如果是return,编译器将使其调用进程退出的代码(如_exit()),从而导致进程及其所有线程结束运行。
稳定、高效、健壮
=============================================================
========================================================================
=====================================================================
C/C++:pthread_join()和pthread_detach()的区别
2018年08月23日 16:01:12 everlastxc 阅读数 1754
简单来说:pthread_detach()和pthread_join()就是控制子线程回收资源的两种不同的方式。同一进程间的线程具有共享和独立的资源,其中共享的资源有堆、全局变量、静态变量、文件等公用资源。而独享的资源有栈和寄存器,这两种方式就是决定子线程结束时如何回收独享的资源。
pthread_detach()即主线程与子线程分离,两者相互不干涉,子线程结束同时子线程的资源自动回收。pthread_join()即是子线程合入主线程,主线程会一直阻塞,直到子线程执行结束,然后回收子线程资源,并继续执行。
代码编译过程为(文件取名main.cc)
-
/** 编译 -lpthread为链接pthread**/
-
g++ -o a.out main.cc -lpthread
-
/** 执行 **/
-
./a.out
linux下内存占用查看指令为
-
/** 先找到想要查看的程序的进程号 **/
-
ps -ef
-
/** -d 1 表示数据刷新频率1秒 -p xxx 表示想查看的程序的进程号 **/
-
top -d 1 -p xxx
测试程序如下
-
// for test
-
#include <iostream>
-
#include <unistd.h>
-
#include <pthread.h>
-
using namespace std;
-
void* svc(void* args)
-
{
-
sleep(3);
-
cout << "111111111111111" << endl;
-
return nullptr;
-
}
-
int main()
-
{
-
do
-
{
-
pthread_t threadId = 0;
-
const int ret = pthread_create(&threadId, 0, &svc, nullptr);
-
if (ret == -1)
-
{
-
cout << "error while create thread!" << endl;
-
return 0;
-
}
-
sleep(1)
-
// pthread_join(threadId, nullptr);
-
// cout << "222222222222222" << endl;
-
// pthread_detach(threadId);
-
// cout << "333333333333333" << endl;
-
}while (false);
-
getchar();
-
return 0;
-
}
先注销掉pthread_join()和pthread_detach()两个函数,代码中每隔1s创建一个子线程,并且子线程在执行结束后资源并没有被释放,变成了僵尸线程,此时用top明明查看进程的内存占用,发现进程占用的内存不断增大。此时若取消注释pthread_join()或者是pthread_detach()中的任意一个,再重新编译并启动程序,发现进程内存占用不会增大。因为子线程资源被释放掉了(无论是子线程自动释放还是主线程回收,都是释放)。
然后在一个子线程中观察两种释放的区别(此时不再创建多个子线程)
-
// for test
-
#include <iostream>
-
#include <unistd.h>
-
#include <pthread.h>
-
using namespace std;
-
void* svc(void* args)
-
{
-
sleep(3);
-
cout << "111111111111111" << endl;
-
return nullptr;
-
}
-
int main()
-
{
-
do
-
{
-
pthread_t threadId = 0;
-
const int ret = pthread_create(&threadId, 0, &svc, nullptr);
-
if (ret == -1)
-
{
-
cout << "error while create thread!" << endl;
-
return 0;
-
}
-
sleep(1)
-
pthread_join(threadId, nullptr);
-
cout << "222222222222222" << endl;
-
// pthread_detach(threadId);
-
// cout << "222222222222222" << endl;
-
} while (false);
-
// getchar();
-
return 0;
-
}
主线程调用pthread_join()时,主线程将阻塞在这条调用语句上,不再执行下面的cout,直到子线程执行完毕后,主线程回收了子线程的资源,才会继续向下执行。这段代码的测试结果是
先输出子线程休眠三秒后打印的111111111111111,后输出pthread_join()语句之后的222222222222222,这样我们发现,在子线程执行完毕之后,主线程才继续执行pthread_join()之后的语句。而如果我们使用pthread_detach(),代码如下
-
// for test
-
#include <iostream>
-
#include <unistd.h>
-
#include <pthread.h>
-
using namespace std;
-
void* svc(void* args)
-
{
-
sleep(3);
-
cout << "111111111111111" << endl;
-
return nullptr;
-
}
-
int main()
-
{
-
do
-
{
-
pthread_t threadId = 0;
-
const int ret = pthread_create(&threadId, 0, &svc, nullptr);
-
if (ret == -1)
-
{
-
cout << "error while create thread!" << endl;
-
return 0;
-
}
-
sleep(1)
-
// pthread_join(threadId, nullptr);
-
// cout << "222222222222222" << endl;
-
pthread_detach(threadId);
-
cout << "222222222222222" << endl;
-
} while (false);
-
// getchar();
-
return 0;
-
}
结果如下
主线程不会等待子线程执行结束就已经执行结束了,因此子线程根本就没有来得及执行,程序就结束了。
我觉得这里可以理解为如果子线程的资源需要主线程来回收的话,那么主线程就一定要等子线程结束,因为子线程还没用完呢你就不能拿去回收,但是如果子线程资源要自动回收的话,那么主线程就不必等你了。
======================================================================
============================================================================
=============================================================================
=======================================================
linux高级编程之线程间的通信(pthread_cleanup_push和pthread_cleanup_pop)
2017年06月05日 21:43:53 那时风起 阅读数 5733
版权声明:转载请注明出处: https://blog.csdn.net/longbei9029/article/details/72871714
linux高级编程之线程间的通信(pthread_cleanup_push和pthread_cleanup_pop)
线程可以安排他退出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的。这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说他们的执行顺序与他们注册的顺序相反。
pthread_cleanup_push和pthread_cleanup_pop函数原型如下:
头文件:#include <pthread.h>
函数原型:void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_clean_pop(int execute);
void(*rtn)(void *): 线程清理函数
另外简单记录下pthread_cancel函数。该函数为线程取消函数,用来取消同一进程中的其他进程,函数原型:
头文件: #include <pthread.h>
函数原型:pthread_cancel(pthread_t tid);
tid: 线程id
当线程执行以下动作时,调用清理函数,调用的参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的。
●调用pthread_exit时
●响应取消请求时
●用非零execute参数调用pthread_cleanup_pop时
关于书上有句原话:“如果execute参数置为0,清理函数将不被调用”,我觉得说的有问题,而且接下来我摘抄了书上的一个例子,刚好验证了他说的这句话的错误,而且我也验证了下,当然在一篇博客中我看到这样的解释觉得很合理:当pthread_cleanup_pop()函数的参数为0时,仅仅在线程调用pthread_exit函数或者其它线程对本线程调用 pthread_cancel函数时,才在弹出“清理函数”的同时执行该“清理函数”。同样我也取了博客中的第二个例子来说明pthread_cancel调用时,pthread_cleanup_push会用清理函数。(ps:重新看了下这个疑问,翻书看了下,其实后面还有句话:“不管上述哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理函数”,所以可能作者想表达的是设置为pop参数设置为0,在其它两种情况下同样会被调用)
参考博客:http://blog.chinaunix.net/uid-26772137-id-3369725.html
例1:
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <pthread.h>
-
void cleanup(void *arg)
-
{
-
printf("cleanup:%s\n",(char*)arg);
-
}
-
void *thr_fn1(void *arg)
-
{
-
printf("thread 1 start\n");
-
pthread_cleanup_push(cleanup,"thread 1 first handler");
-
pthread_cleanup_push(cleanup,"thread 1 second handler");
-
printf("thread 1 push complete\n");
-
if(arg)
-
return ((void *)1);
-
pthread_cleanup_pop(0);
-
pthread_cleanup_pop(0);
-
return ((void *)1);
-
}
-
void *thr_fn2(void *arg)
-
{
-
printf("thread 2 start\n");
-
pthread_cleanup_push(cleanup,"thread 2 first handler");
-
pthread_cleanup_push(cleanup,"thread 2 second handler");
-
printf("thread 2 push complete\n");
-
if(arg)
-
pthread_exit((void *)2);
-
pthread_cleanup_pop(0);
-
pthread_cleanup_pop(0);
-
pthread_exit((void *)2);
-
}
-
int main()
-
{
-
int err;
-
pthread_t tid1,tid2;
-
void *tret;
-
err = pthread_create(&tid1,NULL,thr_fn1,(void *)1);
-
if(err != 0)
-
{
-
fprintf(stderr,"thread create 1 is error\n");
-
return -1;
-
}
-
err = pthread_create(&tid2,NULL,thr_fn2,(void *)1);
-
if(err != 0)
-
{
-
fprintf(stderr,"thread create 2 is error\n");
-
return -2;
-
}
-
err = pthread_join(tid1,&tret);
-
if(err != 0)
-
{
-
fprintf(stderr,"can't join with thread 1\n");
-
return -2;
-
}
-
//pthread_cancel(tid1);
-
printf("thread 1 exit code %d\n",tret);
-
err = pthread_join(tid2,&tret);
-
if(err != 0)
-
{
-
fprintf(stderr,"can't join with thread 2\n");
-
return -2;
-
}
-
printf("thread 2 exit code %d\n",tret);
-
return 0;
-
}
运行结果如下:
从输出结果可以看出:两个线程都调用了,但是却只调用了第二个线程的清理处理程序,所以如果线程是通过从它的启动历程中返回而终止的话,那么它的清理处理程序就不会被调用,还要注意清理程序是按照与它们安装时相反的顺序被调用的。从代码输出也可以看到先执行的thread 2 second handler后执行的thread 2 first handler。
例2:
-
#include<stdlib.h>
-
#include<stdio.h>
-
#include<unistd.h>
-
#include<pthread.h>
-
void clean_fun1(void * arg)
-
{
-
printf("this is clean fun1\n");
-
}
-
void clean_fun2(void * arg)
-
{
-
printf("this is clean fun2\n");
-
}
-
void * thread_fun(void * arg)
-
{
-
pthread_cleanup_push(clean_fun1,NULL);
-
pthread_cleanup_push(clean_fun2,NULL);
-
sleep(100);
-
//这里要注意,如果将sleep(100);换成while(1);的话,程序会一直暂停.push和pop要成对出现.
-
//因为while(1);运行的太快,线程不接受cancel信号
-
//while(1);
-
pthread_cleanup_pop(0);
-
pthread_cleanup_pop(0);
-
return NULL;
-
}
-
int main()
-
{
-
pthread_t tid1;
-
int err;
-
err=pthread_create(&tid1,NULL,thread_fun,NULL);
-
if(err!=0)
-
{
-
perror("pthread_create");
-
exit(0);
-
}
-
sleep(3);
-
//printf("test\n");
-
err=pthread_cancel(tid1);
-
if(err!=0)
-
{
-
perror("cancel error:");
-
exit(0);
-
}
-
err=pthread_join(tid1,NULL);
-
if(err!=0)
-
{
-
perror("pthread_join error:");
-
exit(0);
-
}
-
return 0;
-
}
运行结果如下:
从上面也可以看出,当调用pthread_cancel函数请求后,等到响应请求时,代码调用了pthread_clean_push函数中的clean_fun1和clean_fun2,函数clean_fun2中的语句先被打印。
=======================================================
==============================================================================
===============================================================================
============================================================
线程取消(pthread_cancel)
基本概念
pthread_cancel调用并不等待线程终止,它只提出请求。线程在取消请求(pthread_cancel)发出后会继续运行,
直到到达某个取消点(CancellationPoint)。取消点是线程检查是否被取消并按照请求进行动作的一个位置.
与线程取消相关的pthread函数
int pthread_cancel(pthread_t thread)
发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。
int pthread_setcancelstate(int state, int *oldstate)
设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,
分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。
int pthread_setcanceltype(int type, int *oldtype)
设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。
void pthread_testcancel(void)
是说pthread_testcancel在不包含取消点,但是又需要取消点的地方创建一个取消点,以便在一个没有包含取消点的执行代码线程中响应取消请求.
线程取消功能处于启用状态且取消状态设置为延迟状态时,pthread_testcancel()函数有效。
如果在取消功能处处于禁用状态下调用pthread_testcancel(),则该函数不起作用。
请务必仅在线程取消线程操作安全的序列中插入pthread_testcancel()。除通过pthread_testcancel()调用以编程方式建立的取消点意外,pthread标准还指定了几个取消点。测试退出点,就是测试cancel信号.
取消点:
线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。
线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。
pthreads标准指定了几个取消点,其中包括:
(1)通过pthread_testcancel调用以编程方式建立线程取消点。
(2)线程等待pthread_cond_wait或pthread_cond_timewait()中的特定条件。
(3)被sigwait(2)阻塞的函数
(4)一些标准的库调用。通常,这些调用包括线程可基于阻塞的函数。
缺省情况下,将启用取消功能。有时,您可能希望应用程序禁用取消功能。如果禁用取消功能,则会导致延迟所有的取消请求,
直到再次启用取消请求。
根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及
read()、write()等会引起阻塞的系统调用都是Cancelation-point,而其他pthread函数都不会引起Cancelation动作。
但是pthread_cancel的手册页声称,由于LinuxThread库与C库结合得不好,因而目前C库函数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调用中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用pthread_testcancel(),从而达到POSIX标准所要求的目标.
即如下代码段:
pthread_testcancel();
retcode = read(fd, buffer, length);
pthread_testcancel();
注意:
程序设计方面的考虑,如果线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止。因此在这样的循环体的必经路径上应该加入pthread_testcancel()调用.
取消类型(Cancellation Type)
我们会发现,通常的说法:某某函数是 Cancellation Points,这种方法是容易令人混淆的。
因为函数的执行是一个时间过程,而不是一个时间点。其实真正的 Cancellation Points 只是在这些函数中 Cancellation Type 被修改为 PHREAD_CANCEL_ASYNCHRONOUS 和修改回 PTHREAD_CANCEL_DEFERRED 中间的一段时间。
POSIX的取消类型有两种,一种是延迟取消(PTHREAD_CANCEL_DEFERRED),这是系统默认的取消类型,即在线程到达取消点之前,不会出现真正的取消;另外一种是异步取消(PHREAD_CANCEL_ASYNCHRONOUS),使用异步取消时,线程可以在任意时间取消。
线程终止的清理工作
Posix的线程终止有两种情况:正常终止和非正常终止。
线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;
非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。
不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。
最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,或者在打开独占锁以前的运行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。
在POSIX线程API中提供了一个pthread_cleanup_push()/ pthread_cleanup_pop()函数,
对用于自动释放资源—从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。
API定义如下:
void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)
pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数
在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push() 的调用将在清理函数栈中形成一个函数链;
从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终止动作(包括调用pthread_exit()和异常终止,不包括return)
都将执行pthread_cleanup_push()所指定的清理函数。
在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到 pthread_cleanup_pop()时
是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。
pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:
#define pthread_cleanup_push(routine,arg) \
{
struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
_pthread_cleanup_pop (&_buffer, (execute)); \
}
可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。
在下面的例子里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。
pthread_cleanup_push(pthread_mutex_unlock, (void*) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_mutex_unlock(&mut);
pthread_cleanup_pop(0);
或者
void cleanup(void *arg)
{
pthread_mutex_unlock(&mutex);
}
void* thread0(void* arg)
{
pthread_cleanup_push(cleanup, NULL); // thread cleanup handler p
thread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0);
pthread_exit(NULL);
}
============================================================
================================================================================
==============================================================
线程清理函数pthread_cleanup_push / pthread_cleanup_pop
2014年10月17日 11:45:52 b_h_l 阅读数 1340
线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序,线程可以建立多个清理处理程序。处理程序记录在栈中,也就是说它们的执行顺序与它们注册时的顺序相反。
pthread_cleanup_push来注册清理函数rtn,这个函数有一个参数arg。在以下三种情形之一发生时,注册的清理函数被执行:
1)调用pthread_exit。
2)作为对取消线程请求(pthread_cancel)的响应。
3)以非0参数调用pthread_cleanup_pop。
注意:
1)如果线程只是由于简单的返回而终止的,则清除函数不会被调用。
2)如果pthread_cleanup_pop被传递0参数,则清除函数不会被调用,但是会清除处于栈顶的清理函数。
名称: | pthread_cleanup_push / pthread_cleanup_pop |
功能: | 线程清理处理程序 |
头文件: | #include <pthread.h> |
函数原形: | void pthread_cleanup_push(void (*rtn)(void *),void *arg); void pthread_cleanup_pop(int execute); |
参数: | rtn 处理程序入口地址 arg 传递给处理函数的参数 |
返回值: | 无 |
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <pthread.h>
-
#include <string.h>
-
void cleanup(void *arg){
-
printf("cleanup: %s/n",(char *)arg);
-
}
-
void *thr_fn1(void *arg){
-
printf("thread 1 start/n");
-
pthread_cleanup_push(cleanup,"thread 1 first handler");
-
pthread_cleanup_push(cleanup,"thread 1 second handler");
-
printf("thread 1 push complete/n");
-
if(arg)
-
return ((void *)1);
-
pthread_cleanup_pop(1);
-
pthread_cleanup_pop(1);
-
return ((void *)1);
-
}
-
void *thr_fn2(void *arg){
-
printf("thread 2 start/n");
-
pthread_cleanup_push(cleanup,"thread 2 first handler");
-
pthread_cleanup_push(cleanup,"thread 2 second handler");
-
printf("thread 2 push complete/n");
-
if(arg){
-
pthread_exit((void *)2);
-
}
-
pthread_cleanup_pop(0); //取消第一个线程处理程序
-
pthread_cleanup_pop(0); //取消第二个线程处理程序
-
pthread_exit((void *) 2);
-
}
-
int main(void){
-
int err;
-
pthread_t tid1,tid2;
-
void *tret;
-
err = pthread_create(&tid1,NULL,thr_fn1,(void *)1);
-
if( err != 0){
-
fprintf(stderr,"create thread1 failed: %s",strerror(err));
-
exit(1);
-
}
-
err = pthread_create(&tid2,NULL,thr_fn2,(void *)2);
-
if(err != 0){
-
fprintf(stderr,"create thread 2 failed: %s",strerror(err));
-
exit(1);
-
}
-
err = pthread_join(tid1,&tret);
-
if(err != 0){
-
fprintf(stderr,"thread1 join failed: %s",strerror(err));
-
exit(1);
-
}
-
printf("thread 1 exit code %d/n",(int)tret);
-
err = pthread_join(tid2,&tret);
-
if(err != 0){
-
fprintf(stderr,"thread2 join failed: %s",strerror(err));
-
exit(1);
-
}
-
printf("thread 2 exit code %d/n",(int) tret);
-
exit(0);
-
}
==============================================================
=================================================================================