8

在 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(&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

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

4

2 回答 2

3

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

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

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

于 2012-08-21T09:55:54.567 回答
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为变量创建 __thread 持续时间空间
  2. 在给定线程的第一个条目上,我们使用placement new 在存储中构造变量
  3. 我们还__thread为放置删除调用分配了一个链表条目
  4. 我们使用pthread_setspecific来跟踪每个线程列表头
  5. 当线程退出时,传递给的函数pthread_key_create遍历调用放置删除的列表。

...

#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 扩展。
于 2012-08-21T11:30:15.840 回答