zoukankan      html  css  js  c++  java
  • 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]
    ", __func__, (char*)arg);
        } else {
            printf("%s()
    ", __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]
    ", (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]
    ", (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]
    ", (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]
    ", (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) 就会被跳出,后面的代码肯定也就不会被执行了。

    同步地址:https://www.fengbohello.top/archives/linux-pthread-cleanup

  • 相关阅读:
    C++11中静态局部变量初始化的线程安全性
    213. 打家劫舍 II
    cas解决aba相关问题
    socket[可读可写异常]3种条件的发生
    linux信号处理 (信号产生 信号阻塞 信号集)
    vim set paste解决粘贴乱序乱码问题
    174. 地下城游戏
    208. 实现 Trie (前缀树) 和 面试题 17.13. 恢复空格
    Centos安装和卸载docker
    Go语言轻量级框架-Gin与入门小案例MySQL增删查改
  • 原文地址:https://www.cnblogs.com/fengbohello/p/7571730.html
Copyright © 2011-2022 走看看