【问题标题】:Why is the destructor passed in through pthread_key_create being called only by children thread but the main thread?为什么通过pthread_key_create传入的析构函数只被子线程调用,主线程调用?
【发布时间】:2020-03-18 22:00:54
【问题描述】:

代码:

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] = '\0';          /* 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,但子线程会。我想知道为什么?

谢谢

【问题讨论】:

  • 标题不清楚。主线程中没有调用析构函数?
  • 当我说析构函数时,我的意思是传递给 pthread_key_create(&strerrorKey, destructor) 的函数
  • @q0987,我认为托尼的问题是关于“但主线程”的预期含义。即使没有,这也是想知道的。
  • 我的第二个问题是关于为什么你会期望析构函数永远被调用,尤其是为什么你期望调用strerror()会产生这样的结果效果。您的main()threadFunc() 似乎都没有直接或间接地做任何会产生这种结果的事情。我希望您代表的成员非常清楚我们需要minimal reproducible example,以及如何准备一个好的。
  • @JohnBollinger 这正是我要问的。

标签: c linux pthreads


【解决方案1】:

这是一个简化的例子:

#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;
}

根据pthread_key_create 的手册页

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

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

但是主线程默认退出的方式,没有调用key具体的析构函数。请参阅 pthread_create man pagequestion 24521968。在 main 结束时调用 pthread_exit() 将调用析构函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-06
    • 2015-09-23
    • 1970-01-01
    • 2021-10-09
    • 2014-08-22
    • 2013-12-11
    • 2020-02-20
    相关资源
    最近更新 更多