9

根据pthread_key_create手册页,我们可以关联一个析构函数,以便在线程关闭时调用。我的问题是我注册的析构函数没有被调用。我的代码要点如下。

static pthread_key_t key;
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT;

void destructor(void *t) {
  // thread local data structure clean up code here, which is not getting called
}

void create_key() {
  pthread_key_create(&key, destructor);
}

// This will be called from every thread
void set_thread_specific() {

  ts = new ts_stack; // Thread local data structure

  pthread_once(&tls_init_flag, create_key);
  pthread_setspecific(key, ts);
}

知道什么可能会阻止调用此析构函数吗?我现在也在使用 atexit() 在主线程中进行一些清理。是否有可能干扰调用析构函数?我也尝试删除它。仍然没有工作。我也不清楚我是否应该将主线程作为一个单独的案例与 atexit 一起处理。(顺便说一句,必须使用 atexit,因为我需要在应用程序退出时进行一些特定于应用程序的清理)

4

6 回答 6

3

这是设计使然。

主线程退出(通过返回或调用exit()),并且不使用pthread_exit(). POSIX 文档pthread_exit调用线程特定的析构函数。

可以pthread_exit()在末尾添加main。或者,您可以使用atexit来进行销毁。在这种情况下,将特定于线程的值设置为是干净的,NULL这样在pthread_exit调用 的情况下,该键的销毁不会发生两次。

更新实际上,我通过简单地将其添加到我的全局单元测试设置功能中解决了我的直接担忧:

::atexit([] { ::pthread_exit(0); });

因此,在我的全局夹具类的上下文中MyConfig

struct MyConfig {
    MyConfig()   {
        GOOGLE_PROTOBUF_VERIFY_VERSION;
        ::atexit([] { ::pthread_exit(0); });
    }
    ~MyConfig()  { google::protobuf::ShutdownProtobufLibrary(); }
};

使用的一些参考资料:


PS。当然引入了<thread>c++11 ,因此您可以使用更好、更便携的原语。

于 2016-04-05T23:14:50.800 回答
2

它已经在sehe的答案中,只是为了以紧凑的方式呈现关键点:

  • pthread_key_create()析构函数调用由对pthread_exit().
  • 如果线程的启动例程返回,则行为就像pthread_exit()被调用(即,触发析构函数调用)。
  • 但是,如果main()返回,则行为就像exit()被调用一样——不会触发析构函数调用。

这在http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html中有解释。另请参见 C++17 6.6.1p5 或 C11 5.1.2.2.3p1。

于 2018-01-31T12:57:03.830 回答
0

我写了一个快速测试,我唯一改变的就是将create_key你的调用移到set_thread_specific.

也就是说,我在主线程中调用了它。

然后我看到当线程例程退出时我的destroy被调用。

于 2014-07-02T02:44:54.467 回答
0

您最初使用 atexit 将主线程作为单独案例处理的想法对我来说效果最好。

请注意 pthread_exit(0) 会覆盖进程的退出值。例如,即使 main() 返回数字 3,以下程序也会以状态 0 退出:

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

class ts_stack {
public:
  ts_stack () {
    printf ("init\n");
  }
  ~ts_stack () {
    printf ("done\n");
  }
};

static void cleanup (void);

static pthread_key_t key;
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT;

void destructor(void *t) {
  // thread local data structure clean up code here, which is not getting called
  delete (ts_stack*) t;
}

void create_key() {
  pthread_key_create(&key, destructor);
  atexit(cleanup);
}

// This will be called from every thread
void set_thread_specific() {
  ts_stack *ts = new ts_stack (); // Thread local data structure

  pthread_once(&tls_init_flag, create_key);
  pthread_setspecific(key, ts);
}

static void cleanup (void) {
  pthread_exit(0); // <-- Calls destructor but sets exit status to zero as a side effect!
}

int main (int argc, char *argv[]) {
  set_thread_specific();
  return 3; // Attempt to exit with status of 3
}
于 2017-05-29T22:13:30.973 回答
0

我有和你类似的问题:pthread_setspecific设置一个键,但析构函数永远不会被调用。为了解决这个问题,我们简单地切换到thread_localC++。如果更改太复杂,您也可以执行以下操作:

例如,假设您有一些类ThreadData,您希望在线程完成执行时执行一些操作。您在这些行上定义了析构函数:

void destroy_my_data(ThreadlData* t) {
   delete t;
}

当您的线程启动时,您分配内存ThreadData*并为其分配一个析构函数,如下所示:

ThreadData* my_data = new ThreadData;
thread_local ThreadLocalDestructor<ThreadData> tld;
tld.SetDestructorData(my_data, destroy_my_data);
pthread_setspecific(key, my_data)

请注意,它ThreadLocalDestructor被定义为 thread_local。我们依靠C++11的机制,当线程退出时,ThreadLocalDestructor会自动调用析构函数,并~ThreadLocalDestructor实现调用函数destroy_my_data

下面是 ThreadLocalDestructor 的实现:

template <typename T>
class ThreadLocalDestructor
{
public:
    ThreadLocalDestructor() : m_destr_func(nullptr), m_destr_data(nullptr)
    {
    }

    ~ThreadLocalDestructor()
    {
        if (m_destr_func) {
            m_destr_func(m_destr_data);
        }
    }
    void SetDestructorData(void (*destr_func)(T*), T* destr_data)
    {
        m_destr_data = destr_data;
        m_destr_func = destr_func;
    }

private:
    void (*m_destr_func)(T*);
    T* m_destr_data;
};
于 2019-05-20T09:09:28.107 回答
0

我在main()结束时手动调用destructor( ) :

void * ThreadData = NULL;

if ((ThreadData = pthread_getspecific(key)) != NULL)
        destructor(ThreadData);

当然key应该在main()代码的早期正确初始化。PS。在main()的末尾调用Pthread_Exit( )似乎会挂起整个应用程序......

于 2016-11-10T14:58:49.693 回答