pthread_key_create() - “析构函数”何时被调用?

3

我编写了一个程序来学习Linux (Linux 3.13.0-24-generic #46-Ubuntu)上的线程特定数据,代码如下:

我尝试在传递给pthread_key_create()的析构函数中打印线程ID,但似乎只有子线程成功打印,主线程没有打印这个信息。

我的问题是:

析构函数是在线程终止之前还是之后调用的?

主线程为什么没有打印这些信息,是因为主线程已经被销毁了吗?

tsd_test.c

// test of thread-specific data

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>

static pthread_once_t once = PTHREAD_ONCE_INIT;
static pthread_key_t tidKey;

static void destructor(void *buf) {
    unsigned long *_tid = buf;
    printf("destroy, tid: %lu\n", *_tid);
    free(buf);
}

static void createKey(void) {
    int s = pthread_key_create(&tidKey, destructor);
    if(s != 0) {
        printf("failed to create key\n");
        exit(-1);
    }
}

void *store_tid() {
    int s;
    unsigned long *buf;

    // create key
    s = pthread_once(&once, createKey);
    if(s != 0) {
        printf("failed to create key\n");
        exit(-1);
    }

    buf = pthread_getspecific(tidKey);
    if(buf == NULL) { // thread call this function for the first time,
        buf = malloc(sizeof(unsigned long));
        if(buf == NULL) {
            printf("failed to allocate memory, %s\n", strerror(errno));
            exit(-1);
        }
        // register buffer to specified key & current thread,
        s = pthread_setspecific(tidKey, buf);
        if(s != 0) {
            printf("failed to setspecific\n");
            exit(-1);
        }
    }

    // store tid to buffer,
    *buf = (unsigned long)pthread_self();
    printf("set tid to: %lu\n", *buf);

    return buf;
}

void tsd_test() {
    unsigned long *tidp_a = store_tid();
    printf("tid - before call another thread: %lu\n", *tidp_a);

    int s;
    pthread_t t2;
    s = pthread_create(&t2, NULL, &store_tid, NULL);
    if(s != 0) {
        printf("failed to create thread\n");
        exit(-1);
    }

    s = pthread_join(t2, NULL);
    if(s != 0) {
        printf("failed to join thread\n");
        exit(-1);
    }

    printf("tid - after call another thread: %lu\n", *tidp_a);
}

int main(int argc, char *argv[]) {
    tsd_test();
    return 0;
}

编译:

gcc -pthread tsd_test.c

输出:

set tid to: 3076318976
tid - before call another thread: 3076318976
set tid to: 3076315968
destroy, tid: 3076315968
tid - after call another thread: 3076318976

你可以看到,只有子线程打印了“destroy”,而主线程没有打印。

1
OT: pthread函数所需的签名是void*(*)(void*),因此应该是void *store_tid(void*)。使用所有警告(-Wall -Wextra -pedantic对于gcc)编译以便在出现此类问题时得到通知。 - alk
@alk 懂了,好的提示。 - Eric
1个回答

5

当线程退出时,线程析构函数被调用,而不是在进程死亡时调用,也就是说,当main()函数退出时,整个进程死亡。因此析构函数将不会被调用。

main()函数的结尾或tst()函数的结尾(两者实际上是相同的)调用pthread_exit(NULL);。现在,您将看到析构函数被调用。


我想知道为什么 pthread_exit() 会有所不同?它在终止线程之前调用了 析构函数,还是...? - Eric
如果通过pthread_create()创建线程或调用pthread_exit();退出线程,将隐式调用析构函数(如果有)。但是从main()返回或退出不会这样做,它会退出整个进程。 POSIX关于pthread_exit()的说明是:在执行所有取消清理处理程序之后,如果线程具有任何特定于线程的数据,则将以未指定的顺序调用适当的析构函数。 - P.P
我在 man 手册中看到,我猜它会先调用 destructor,然后触发主线程或子线程的终止。 - Eric
1
从主线程(main())返回(相当于调用exit())并不等同于从另一个线程返回(相当于调用pthread_exit())。 - P.P
1
我的意思是,如果使用pthread_exit(),那么pthread_exit()将在终止线程之前首先调用析构函数。我不确定这是否正确,但这似乎是合理的。 - Eric
我猜在主线程中使用 pthread_exit() 会出现另一个问题:程序中全局和静态变量的析构函数不会被调用。这些析构函数可能会完成一些资源的清理工作,例如将缓冲区刷新到文件等。 - Serge Rogatch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接