POSIX 多线程的 cleanup 函数
控制清理函数的函数有两个,一个是 pthread_cleanup_push(), 用来把清理函数压入栈中,另一个是 pthread_cleanup_pop(), 用来把栈中的函数弹出来。
用这两个函数组合,可以达到在线程退出时,清理线程数据的作用, 例如对 mutex 进行解锁等。
下面是这两个函数的函数原型:
#include<pthread.h>void pthread_cleanup_push(void(*routine)(void*),void*arg);void pthread_cleanup_pop(int execute);//Compile and link with -pthread.
我们先写个简单的例子,感性认识一下这两个函数的作用:
#include<stdio.h>#include<pthread.h>void handlers(void*arg){if(NULL != arg){
printf("%s() : [%s]\n", __func__,(char*)arg);}else{
printf("%s()\n", __func__);}}void*
thread_start(void*arg){
pthread_cleanup_push(handlers,"one");
pthread_cleanup_push(handlers,"two");
pthread_cleanup_push(handlers,"thr");
printf("This is thread [%u]\n",(unsignedint)pthread_self());
pthread_exit("he~he~");//do something
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);
pthread_cleanup_pop(1);return NULL;}int main(){pthread_t pt;
pthread_create(&pt, NULL, thread_start, NULL);void*r = NULL;
pthread_join(pt,&r);if(NULL != r){
printf("thread return : [%s]\n",(constchar*)r);}return0;}
编译并运行:
Thisis thread [3290769152]
handlers():[thr]
handlers():[two]
handlers():[one]
thread return:[he~he~]
我们在代码里面是按照 one、two、thr 的顺序调用的 pthread_cleanup_push() 函数, 结果在运行后得到的结果中,却看到它们输出的顺序正好倒过来了。 这正是这对函数的性质。
并且这对函数还有一个性质,那就是使用 pthread_cleanup_push() 和 pthread_cleanup_pop() 之间使用 return 的话,会导致之后的 pthread_cleanup_pop() 不起作用。 这是为什么呢?原因是,其实 pthread_cleanup_push() 和 pthread_cleanup_pop() 不是函数, 而是一对宏。
其宏定义在头文件 pthread.h 中可以看到,宏定义如下:
# define pthread_cleanup_push(routine, arg) \do{ __pthread_cleanup_class __clframe (routine, arg)# define pthread_cleanup_pop(execute) \
__clframe.__setdoit (execute); }while(0)
我们写个更简单的程序,把这两个宏展开后看一看是什么样结果:
代码如下:
#include<stdio.h>#include<pthread.h>void hand(void* arg){
printf("do nothing");}void*thread_start(void* arg){
pthread_cleanup_push(hand,"a");
printf("This is thread [%u]\n",(unsignedint)pthread_self());
pthread_cleanup_pop(1);return NULL;}int main(){return0;}
编译:
gcc -g -E -o pthread_cleanup_macro.i pthread_cleanup_macro.c
查看 pthread_cleanup_macro.i 的代码:
void hand(void* arg){
printf("do nothing");}void*thread_start(void* arg){do{__pthread_unwind_buf_t __cancel_buf;void(*__cancel_routine)(void*)=(hand);void*__cancel_arg =("a");int __not_first_call = __sigsetjmp ((struct __jmp_buf_tag *)(void*) __cancel_buf.__cancel_jmp_buf,0);if(__builtin_expect((__not_first_call),0)){ __cancel_routine (__cancel_arg); __pthread_unwind_next (&__cancel_buf);} __pthread_register_cancel (&__cancel_buf);do{;
printf("This is thread [%u]\n",(unsignedint)pthread_self());do{}while(0);}while(0); __pthread_unregister_cancel (&__cancel_buf);if(1) __cancel_routine (__cancel_arg);}while(0);return((void*)0);}int main(){return0;}
可以看到,thread_start 函数里面的 pthread_cleanup_push() 和 pthread_cleanup_pop() 已经被展开了。我们把 thread_start 函数里面的代码再修饰一下格式,结果如下:
void*thread_start(void* arg){do{__pthread_unwind_buf_t __cancel_buf;void(*__cancel_routine)(void*)=(hand);void*__cancel_arg =("a");int __not_first_call = __sigsetjmp ((struct __jmp_buf_tag *)(void*) __cancel_buf.__cancel_jmp_buf,0);if(__builtin_expect((__not_first_call),0)){
__cancel_routine (__cancel_arg);
__pthread_unwind_next (&__cancel_buf);}
__pthread_register_cancel (&__cancel_buf);do{;
printf("This is thread [%u]\n",(unsignedint)pthread_self());do{}while(0);}while(0);
__pthread_unregister_cancel (&__cancel_buf);if(1) __cancel_routine (__cancel_arg);}while(0);return((void*)0);}
可以看到,我们输出线程信息的 printf 语句,被一层层的 do{}while(0) 给包围了。 如果在 pthread_cleanup_push() 和 pthread_cleanup_pop() 之间加一个 return , 那么整个 do{}while(0) 就会被跳出,后面的代码肯定也就不会被执行了。