c-为什么通过pthread_key_create传递的析构函数只由子线程调用,而由主线程调用



代码:

http://man7.org/tlpi/code/online/book/threads/strerror_tsd.c

static void                         /* Free thread-specific data buffer */
destructor(void *buf)
{
free(buf);
}
static void                         /* One-time key creation function */
createKey(void)
{
int s;
/* Allocate a unique thread-specific data key and save the address
of the destructor for thread-specific data buffers */
s = pthread_key_create(&strerrorKey, destructor);
if (s != 0)
errExitEN(s, "pthread_key_create");
}
char *
strerror(int err)
{
int s;
char *buf;
/* Make first caller allocate key for thread-specific data */
s = pthread_once(&once, createKey);
if (s != 0)
errExitEN(s, "pthread_once");
buf = pthread_getspecific(strerrorKey);
if (buf == NULL) {          /* If first call from this thread, allocate
buffer for thread, and save its location */
buf = malloc(MAX_ERROR_LEN);
if (buf == NULL)
errExit("malloc");
s = pthread_setspecific(strerrorKey, buf);
if (s != 0)
errExitEN(s, "pthread_setspecific");
}
if (err < 0 || err >= _sys_nerr || _sys_errlist[err] == NULL) {
snprintf(buf, MAX_ERROR_LEN, "Unknown error %d", err);
} else {
strncpy(buf, _sys_errlist[err], MAX_ERROR_LEN - 1);
buf[MAX_ERROR_LEN - 1] = '';          /* Ensure null termination */
}
return buf;
}

http://man7.org/tlpi/code/online/dist/threads/strerror_test.c

static void *
threadFunc(void *arg)
{
char *str;
printf("Other thread about to call strerror()n");
str = strerror(EPERM);
printf("Other thread: str (%p) = %sn", str, str);
return NULL;
}
int
main(int argc, char *argv[])
{
pthread_t t;
int s;
char *str;
str = strerror(EINVAL);
printf("Main thread has called strerror()n");
s = pthread_create(&t, NULL, threadFunc, NULL);
if (s != 0)
errExitEN(s, "pthread_create");
s = pthread_join(t, NULL);
if (s != 0)
errExitEN(s, "pthread_join");
/* If strerror() is not thread-safe, then the output of this printf() be
the same as that produced by the analogous printf() in threadFunc() */
printf("Main thread:  str (%p) = %sn", str, str);
exit(EXIT_SUCCESS);
}

问题:

int pthread_key_create(pthread_key_t *key, void (destructor)(void *));

strerror的实现使用特定于线程的数据。在终止具有与键相关联的非NULL值的线程时析构函数由Pthreads API自动调用,并且给定值作为其参数。根据我的测试,调用strerror的主线程不会触发destructor,但子线程会触发。我想知道为什么?

谢谢

下面是一个简化的例子:

#include <pthread.h>
#include <stdio.h>
static pthread_key_t key;
void destroy(void *data) 
{
fprintf(stderr, "==> %s on thread %lu with argument %pn", __func__, pthread_self(), data);
}
void *target(void *data)
{
// comment out the following line and destroy will not be called from child thread
pthread_setspecific(key, target);
fprintf(stderr, "%s set key to %p on thread %lun", __func__, pthread_getspecific(key), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
fprintf(stderr, "%s is on thread %lun", __func__, pthread_self());
pthread_key_create(&key, destroy);
pthread_setspecific(key, main);
fprintf(stderr, "%s set key to %pn", __func__, pthread_getspecific(key));
pthread_t child;
pthread_create(&child, NULL, target, NULL);
fprintf(stderr, "main thread created thread %lun",  child);
pthread_join(child, NULL);
fprintf(stderr, "main thread joined childn");
// comment out the following line and destroy will not be called from main thread
pthread_exit(main);
return 0;
}

根据pthread_key_create 的手册页

在线程退出时,如果键值具有非NULL析构函数指针,并且线程具有与该键关联的非NULL值,则该键的值将设置为NULL,然后使用以前关联的值作为其唯一参数来调用所指向的函数。

在问题代码和简化示例中,每个线程都将键设置为非NULL值,因此似乎应该在每个线程上调用析构函数。

但默认情况下,主线程退出的方式是不调用键特定的析构函数。请参阅pthread_create手册页和问题24521968。在main的末尾调用pthread_exit((将调用析构函数。

最新更新