linux pthreads上的gcc 4.7 - 使用__thread的非常重要的thread_local解决方法(无提升)

时间:2012-08-21 06:51:06

标签: c++ linux multithreading c++11 pthreads

在C ++ 11中,你可以拥有一个带有thread_local存储的非平凡对象:

class X { ... }

void f()
{
    thread_local X x = ...;
    ...
}

不幸的是,此功能尚未在gcc中实现(截至4.7)。

gcc允许你拥有线程局部变量,但只允许使用普通类型。

我正在寻找解决方法:

这是我到目前为止所做的:

#include <iostream>
#include <type_traits>

using namespace std;

class X
{
public:
    X() { cout << "X::X()" << endl; };
    ~X() { cout << "X::~X()" << endl; }
};

typedef aligned_storage<sizeof(X), alignment_of<X>::value>::type XStorage;

inline void placement_delete_x(X* p) { p->~X(); }

void f()
{
        static __thread bool x_allocated = false;
        static __thread XStorage x_storage;

        if (!x_allocated)
        {
                new (&x_storage) X;
                x_allocated = true;

                // TODO: add thread cleanup that
                //     calls placement_delete_x(&x_storage)
        }

        X& x = *((X*) &x_storage);
}

int main()
{
        f();
}

我需要帮助的是在退出当前线程时调用placement_delete_x(&amp; x_storage)。我可以使用pthreads和/或linux中的机制来做到这一点吗?我需要在某种pthread清理堆栈中添加函数指针和参数吗?

更新

我认为pthread_cleanup_push可能是我想要的:

http://www.kernel.org/doc/man-pages/online/pages/man3/pthread_cleanup_push.3.html

这会在正确的情况下调用清理处理程序吗?

更新2:

看起来boost::thread_specific_ptr最终使用pthread_key_create参数调用destructor,而不调用pthread_cleanup_push - 调用其tls清理函数:

http://pubs.opengroup.org/onlinepubs/009696799/functions/pthread_key_create.html

目前还不清楚这两种方法之间的区别是什么,如果有的话。 ?

2 个答案:

答案 0 :(得分:3)

pthread_key_create和朋友是你想要用析构函数实现类型的特定于线程的变量。但是,这些通常要求您管理创建和销毁变量的整个过程,我不确定您是否可以将它们与__thread结合使用。

pthread_cleanup_push不合适。如果线程在使用该资源的(短)代码块期间退出,则它旨在允许释放资源;如您链接到的文档中所述,必须在该函数的同一级别与pthread_cleanup_pop匹配,并且如果线程从其主函数返回,则不会调用处理程序功能。这意味着如果您希望线程局部变量在函数调用之间保持不变,则无法使用它。

为了那些没有禁止第三方库的人的利益,Boost提供了一种方便,便携的方式来管理线程本地存储。

答案 1 :(得分:3)

正如迈克所说pthread_cleanup_push不合适。正确的方法是使用pthread_key_create

我已经实施了一个小型演示程序来展示如何做到这一点。我们实现了您使用的宏thread_local

使用真正的C ++ 11功能,它将是:

void f()
{
    thread_local X x(1,2,3);
    ...
}

这是:

void f()
{
    thread_local (X, x, 1, 2, 3);
    ...
}

这和boost :: thread_specifc_ptr之间的区别在于动态内存分配为零。一切都以__thread的持续时间存储。它的重量也明显更轻,但它具有特定的gcc / linux。

概述:

  1. 我们使用std::aligned_storage为变量
  2. 创建__thread持续时间空间
  3. 在给定线程的第一个条目中,我们使用placement new来构建存储中的变量
  4. 我们还__thread将一个链接列表条目分配给展示位置删除调用
  5. 我们使用pthread_setspecific来跟踪每个主题列表头
  6. 传递给pthread_key_create的函数遍历列表,在线程退出时调用位置删除。
  7. ...

    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    static pthread_key_t key;
    static pthread_once_t once_control = PTHREAD_ONCE_INIT;
    
    struct destructor_list
    {
        void (*destructor)(void*);
        void* param;
        destructor_list* next;
    };
    
    static void execute_destructor_list(void* v)
    {
        for (destructor_list* p = (destructor_list*) v; p != 0; p = p->next)
            p->destructor(p->param);
    }
    
    static void create_key()
    {
        pthread_key_create(&key, execute_destructor_list);
    }
    
    void add_destructor(destructor_list* p)
    {
        pthread_once(&once_control, create_key);
    
        p->next = (destructor_list*) pthread_getspecific(key);
        pthread_setspecific(key, p);
    }
    
    template<class T> static void placement_delete(void* t) { ((T*)t)->~T(); }
    
    #define thread_local(T, t, ...)                         \
    T& t = *((T*)                                           \
    ({                                                      \
        typedef typename aligned_storage<sizeof(T),         \
            alignment_of<T>::value>::type Storage;          \
        static __thread bool allocated = false;             \
        static __thread Storage storage;                    \
        static __thread destructor_list dlist;              \
                                                            \
        if (!allocated)                                     \
        {                                                   \
            new (&storage) T(__VA_ARGS__);                  \
            allocated = true;                               \
            dlist.destructor = placement_delete<T>;         \
            dlist.param = &storage;                         \
            add_destructor(&dlist);                         \
        }                                                   \
                                                            \
        &storage;                                           \
    }));
    
    class X
    {
    public:
        int i;
    
        X(int i_in) { i = i_in; cout << "X::X()" << endl; };
    
        void f() { cout << "X::f()" << endl; }
    
        ~X() { cout << "X::~X() i = " << i << endl; }
    };
    
    void g()
    {
        thread_local(X, x, 1234);
        x.f();
    }
    
    int main()
    {
        thread t(g);
        t.join();
    }
    

    注意:

    1. 您需要为每个pthread_ *调用添加错误检查。我刚将它删除以进行展示。
    2. 它使用__thread这是一个GNU扩展
    3. 它使用表达式语句将辅助__thread变量名称保留在父作用域之外。这也是GNU扩展。