首页 > 学院 > 开发设计 > 正文

78-线程清理函数

2019-11-06 06:16:49
字体:
来源:转载
供稿:网友

有时候我们希望线程退出时能够自动的执行某些函数,为了能达到此目的,OS 提供了两个函数帮我们完成这个功能:

void pthread_cleanup_push(void (*rtn)(void*), void *arg);void pthread_cleanup_pop(int execute);

1. 使用方法

如果想要你的线程在退出时能够执行清理函数,你需要使用 pthread_cleanup_push 对你的清理函数进行注册,如下:

void clean(void *arg) { // ...}void *th_fn(void *arg) { // push 和 pop 必须对对出现 pthread_cleanup_push(clean, // 清理函数 clean 的参数); // ... pthread_cleanup_pop(1);}

linux 中,pthread_cleanup_pushpthread_cleanup_pop 这两个函数是通过宏来做的,pthread_cleanup_push 被替换成以左花括号 { 为开头的一段代码,而 pthread_cleanup_pop 被替换成以右花括号 } 结尾的一段代码,这就意味着这两个函数必须要成对出现才能将左右花括号匹配上,否则就出现编译错误。

有些平台可能不是使用宏来实现,就算不成对也没什么关系。

2. 线程清理函数调用时机

有三种情况线程清理函数会被调用:

线程还未执行 pthread_cleanup_pop 前,被 pthread_cancel 取消线程还未执行 pthread_cleanup_pop 前,主动执行 pthread_exit 终止线程执行 pthread_cleanup_pop,且 pthread_cleanup_pop 的参数不为 0.

注意:如果线程还未执行 pthread_cleanup_pop 前通过 return 返回,是不会执行清理函数的。

3. 示例

3.1 程序清单

程序 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 运行结果

3.2 结果分析

从图 1 中的结果可以看到:

当 clean 程序中的线程正常返回时,只有 pthread_cleanup_pop 的参数非 0 时,才会正常执行清理函数。当 clean 程序中的线程在执行 pthread_cleanup_pop 前时,使用 pthread_exit 退出时,清理函数才会被执行,和 pthread_cleanup_pop 的参数没有关系。而使用 return 返回的线程 1 并不会执行清理函数。清理函数的执行顺序,是按照注册时候相反的顺序执行的。

注意,在有些系统中(FreeBSD/Mac OS X),提前终止可能会出现段错误。

4. 总结

掌握线程清理函数的注册方法知道在 Linux 中,push 函数和 pop 函数是通过宏实现的,使用时必须成对出现掌握清理函数的执行时机

练习:完成本文中的实验,编译运行并查看结果。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表