在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
目前还不清楚这两种方法之间的区别是什么,如果有的话。 ?
答案 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。
概述:
std::aligned_storage
为变量__thread
将一个链接列表条目分配给展示位置删除调用pthread_setspecific
来跟踪每个主题列表头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();
}
注意:
__thread
这是一个GNU扩展