有时候我们希望线程退出时能够自动的执行某些函数,为了能达到此目的,OS 提供了两个函数帮我们完成这个功能:
void pthread_cleanup_push(void (*rtn)(void*), void *arg);void pthread_cleanup_pop(int execute);如果想要你的线程在退出时能够执行清理函数,你需要使用 pthread_cleanup_push
对你的清理函数进行注册,如下:
在 linux 中,pthread_cleanup_push
和 pthread_cleanup_pop
这两个函数是通过宏来做的,pthread_cleanup_push
被替换成以左花括号 {
为开头的一段代码,而 pthread_cleanup_pop
被替换成以右花括号 }
结尾的一段代码,这就意味着这两个函数必须要成对出现才能将左右花括号匹配上,否则就出现编译错误。
有些平台可能不是使用宏来实现,就算不成对也没什么关系。
有三种情况线程清理函数会被调用:
线程还未执行 pthread_cleanup_pop 前,被 pthread_cancel 取消线程还未执行 pthread_cleanup_pop 前,主动执行 pthread_exit 终止线程执行 pthread_cleanup_pop,且 pthread_cleanup_pop 的参数不为 0.注意:如果线程还未执行 pthread_cleanup_pop 前通过 return 返回,是不会执行清理函数的。
程序 clean 需要传入两个参数,第 1 个参数表示是否提前返回(在执行 pthread_cleanup_pop 前返回),第 2 个参数表示 pthread_cleanup_pop 的参数。所以有 4 种组合情况。
代码// clean.c#include <unistd.h>#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define ERR(name,err) do{PRintf("%s: %s",#name,strerror(err));exit(-1);}while(0);int excute;void cleanup(void* arg) { printf("cleanup: %s/n", (char*)arg);}void* th_fn1(void* arg) { puts("thread 1 starting"); pthread_cleanup_push(cleanup, "线程 1 清理者 1 号"); pthread_cleanup_push(cleanup, "线程 1 清理者 2 号"); if (arg) { printf("线程 1 提前退出/n"); return (void*)1; } pthread_cleanup_pop(excute); pthread_cleanup_pop(excute); printf("线程 1 正常退出/n"); return (void*)10;}void* th_fn2(void* arg) { puts("thread 2 starting"); pthread_cleanup_push(cleanup, "线程 2 清理者 1 号"); pthread_cleanup_push(cleanup, "线程 2 清理者 2 号"); if (arg) { printf("线程 2 提前退出/n"); pthread_exit((void*)2); } pthread_cleanup_pop(excute); pthread_cleanup_pop(excute); printf("线程 2 正常退出/n"); pthread_exit((void*)20);}int main(int argc, char* argv[]) { if (argc < 3) { printf("Usage: %s <arg 0|1> <excute 0|1>/n", argv[0]); return -1; } pthread_t tid1, tid2; int err; void* ret; void *arg = NULL; excute = 0; arg = (void*)atoi(argv[1]); excute = atoi(argv[2]); err = pthread_create(&tid1, NULL, th_fn1, arg); if (err != 0) ERR(pthread_create1, err); err = pthread_create(&tid2, NULL, th_fn2, arg); if (err != 0) ERR(pthread_create2, err); err = pthread_join(tid1, &ret); if (err != 0) ERR(pthread_join1, err); printf("thread 1 exit code %d/n", (int)ret); err = pthread_join(tid2, &ret); if (err != 0) ERR(pthread_join2, err); printf("thread 2 exit code %d/n", (int)ret); return 0;}编译$ gcc clean.c -o clean -lpthread运行从图 1 中的结果可以看到:
当 clean 程序中的线程正常返回时,只有 pthread_cleanup_pop 的参数非 0 时,才会正常执行清理函数。当 clean 程序中的线程在执行 pthread_cleanup_pop 前时,使用 pthread_exit 退出时,清理函数才会被执行,和 pthread_cleanup_pop 的参数没有关系。而使用 return 返回的线程 1 并不会执行清理函数。清理函数的执行顺序,是按照注册时候相反的顺序执行的。注意,在有些系统中(FreeBSD/Mac OS X),提前终止可能会出现段错误。
练习:完成本文中的实验,编译运行并查看结果。
新闻热点
疑难解答