为什么通过 pthread_key_create 传入的析构函数只被子线程调用而主线程调用?
Why is the destructor passed in through pthread_key_create being called only by children thread but the main thread?
代码:
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] = '[=11=]'; /* 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) = %s\n", 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) = %s\n", 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 %p\n", __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 %lu\n", __func__, pthread_getspecific(key), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
fprintf(stderr, "%s is on thread %lu\n", __func__, pthread_self());
pthread_key_create(&key, destroy);
pthread_setspecific(key, main);
fprintf(stderr, "%s set key to %p\n", __func__, pthread_getspecific(key));
pthread_t child;
pthread_create(&child, NULL, target, NULL);
fprintf(stderr, "main thread created thread %lu\n", child);
pthread_join(child, NULL);
fprintf(stderr, "main thread joined child\n");
// comment out the following line and destroy will not be called from main thread
pthread_exit(main);
return 0;
}
的手册页
At thread exit, if a key value has a non-NULL destructor pointer, and the thread has a non-NULL value associated with that key, the value of the key is set to NULL, and then the function pointed to is called with the previously associated value as its sole argument.
在问题代码和简化示例中,每个线程都将键设置为非 NULL 值,因此似乎应该在每个线程上调用析构函数。
但是主线程默认退出的方式,关键的特定析构函数没有被调用。参见 pthread_create man page and question 24521968。在 main 结束时调用 pthread_exit() 将调用析构函数。
代码:
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] = '[=11=]'; /* 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) = %s\n", 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) = %s\n", 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 %p\n", __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 %lu\n", __func__, pthread_getspecific(key), pthread_self());
return NULL;
}
int main(int argc, char *argv[])
{
fprintf(stderr, "%s is on thread %lu\n", __func__, pthread_self());
pthread_key_create(&key, destroy);
pthread_setspecific(key, main);
fprintf(stderr, "%s set key to %p\n", __func__, pthread_getspecific(key));
pthread_t child;
pthread_create(&child, NULL, target, NULL);
fprintf(stderr, "main thread created thread %lu\n", child);
pthread_join(child, NULL);
fprintf(stderr, "main thread joined child\n");
// comment out the following line and destroy will not be called from main thread
pthread_exit(main);
return 0;
}
的手册页
At thread exit, if a key value has a non-NULL destructor pointer, and the thread has a non-NULL value associated with that key, the value of the key is set to NULL, and then the function pointed to is called with the previously associated value as its sole argument.
在问题代码和简化示例中,每个线程都将键设置为非 NULL 值,因此似乎应该在每个线程上调用析构函数。
但是主线程默认退出的方式,关键的特定析构函数没有被调用。参见 pthread_create man page and question 24521968。在 main 结束时调用 pthread_exit() 将调用析构函数。