g++ 和 gcc 在 pthread_cleanup_push/pop 上的行为不同

Mar*_*ity 1 c c++ gcc g++ pthreads

这是我的代码

#include <pthread.h>
#include <stdio.h>

void cleanup(void *arg) {
    printf("cleanup: %s\n", (const char*)arg);
}

void *thr_fn1(void *arg) {
    printf("thread 1 strat\n");
    pthread_cleanup_push(cleanup, (void*)"thread 1 first handler");
    pthread_cleanup_push(cleanup, (void*)"thread 1 first handler");

    if(arg)
        return (void*)1;

    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);

    return (void*)1;
}

void *thr_fn2(void *arg) {
    printf("thread 2 strat\n");
    pthread_cleanup_push(cleanup, (void*)"thread 2 first handler");
    pthread_cleanup_push(cleanup, (void*)"thread 2 first handler");

    if(arg)
        return (void*)2;

    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);

    return (void*)2;
}

int main() {
    int err;
    pthread_t tid1, tid2;
    void *tret;

    pthread_create(&tid1, NULL, thr_fn1, (void*)1);
    pthread_create(&tid2, NULL, thr_fn2, (void*)1);

    pthread_join(tid1, &tret);
    printf("pthread 1 exit code %ld\n", tret);
    pthread_join(tid2, &tret);
    printf("pthread 2 exit code %ld\n", tret);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

现在我使用gcc和运行它g++

$ gcc main.c -o main
$ ./main
thread 2 strat
thread 1 strat
pthread 1 exit code 1
pthread 2 exit code 2
$ g++ main.c -o main
$ ./main
thread 1 strat
cleanup: thread 1 first handler
cleanup: thread 1 first handler
thread 2 strat
cleanup: thread 2 first handler
cleanup: thread 2 first handler
pthread 1 exit code 1
pthread 2 exit code 2
$ 
Run Code Online (Sandbox Code Playgroud)
  1. 为什么他们的行为不同?
  2. 还有其他函数有类似的行为吗?
  3. gcc我发现和的实现g++是不同的。那么哪一个是更好的实现呢?

S.M*_*.M. 5

在 Linux 上,pthread_cleanup_push()pthread_cleanup_pop()函数被实现为宏,分别扩展为包含{和 的文本}

#  define pthread_cleanup_push(routine, arg) \
  do {                                       \
    __pthread_cleanup_class __clframe (routine, arg)
Run Code Online (Sandbox Code Playgroud)

如果用g++编译,__pthread_cleanup_class是一个C++类:

#ifdef __cplusplus
/* Class to handle cancellation handler invocation.  */
class __pthread_cleanup_class
{
  void (*__cancel_routine) (void *);
  void *__cancel_arg;
  int __do_it;
  int __cancel_type;

 public:
  __pthread_cleanup_class (void (*__fct) (void *), void *__arg)
    : __cancel_routine (__fct), __cancel_arg (__arg), __do_it (1) { }
  ~__pthread_cleanup_class () { if (__do_it) __cancel_routine (__cancel_arg); }
  void __setdoit (int __newval) { __do_it = __newval; }
  void __defer () { pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED,
                                           &__cancel_type); }
  void __restore () const { pthread_setcanceltype (__cancel_type, 0); }
};
Run Code Online (Sandbox Code Playgroud)

它的行为就像任何类一样,它的析构函数在作用域末端运行。

在 C 中,使用 gcc,清理处理程序需要pthread_exit(),但您的代码需要return

  1. 当线程通过调用终止时pthread_exit(3),所有清理处理程序都会按照上一点所述执行。 (如果线程通过从线程启动函数执行返回而终止,则不会调用清理处理程序。)