POSIX 线程清理函数

简介:

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", (unsigned int)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", (const char*)r);
    }

    return 0;
}
复制代码

 

编译并运行:

复制代码
This is 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)
复制代码

 

我们写个更简单的程序,把这两个宏展开后看一看是什么样结果:

代码如下:

复制代码
#  define pthread_cleanup_push(routine, arg) \
  do {                                        \
    __pthread_cleanup_class __clframe (routine, arg)

#  define pthread_cleanup_pop(execute) \
    __clframe.__setdoit (execute);                        \
  } while (0)
复制代码

 

编译:

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", (unsigned int)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() {
    return 0;
}
复制代码

 

可以看到,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", (unsigned int)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) 就会被跳出,后面的代码肯定也就不会被执行了。


本文转自郝峰波博客园博客,原文链接:http://www.cnblogs.com/fengbohello/p/7571730.html,如需转载请自行联系原作者

相关文章
|
6月前
|
Linux C++
LInux下Posix的传统线程示例
LInux下Posix的传统线程示例
51 1
|
6月前
|
Unix API 调度
POSIX线程基本操作
POSIX线程基本操作
86 0
|
1月前
lua面向对象(类)和lua协同线程与协同函数、Lua文件I/O
Lua的面向对象编程、协同线程与协同函数的概念和使用,以及Lua文件I/O操作的基本方法。
32 4
lua面向对象(类)和lua协同线程与协同函数、Lua文件I/O
|
1月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
48 6
|
1月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
25 0
C++ 多线程之线程管理函数
|
6月前
|
存储 安全 数据管理
Linux系统编程教程之Linux线程函数的使用:讲解Linux线程函数
Linux系统编程教程之Linux线程函数的使用:讲解Linux线程函数
67 1
|
3月前
处理串口线程数据的函数
【8月更文挑战第4天】处理串口线程数据的函数。
30 4
|
3月前
|
存储 安全 Unix
并发编程基础:使用POSIX线程(pthread)进行多线程编程。
并发编程基础:使用POSIX线程(pthread)进行多线程编程。
90 0
|
3月前
|
Dart 编译器 API
Dart ffi 使用问题之在C++线程中无法直接调用Dart函数的问题如何解决
Dart ffi 使用问题之在C++线程中无法直接调用Dart函数的问题如何解决
|
6月前
|
设计模式 安全 C++
【C++ const 函数 的使用】C++ 中 const 成员函数与线程安全性:原理、案例与最佳实践
【C++ const 函数 的使用】C++ 中 const 成员函数与线程安全性:原理、案例与最佳实践
288 2

相关实验场景

更多
下一篇
无影云桌面