Linux pthreads 上的 GCC 4.7 - 使用 __thread 的非平凡thread_local解决方法
gcc 4.7 on linux pthreads - nontrivial thread_local workaround using __thread (no boost)
在 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
最终使用 destructor
参数调用pthread_key_create
,而不是pthread_cleanup_push
- 调用其 tls 清理函数:
http://pubs.opengroup.org/onlinepubs/009696799/functions/pthread_key_create.html
目前尚不清楚这两种方法之间的区别是什么,如果有的话。
pthread_key_create
和朋友是你想要用析构函数实现特定于线程的类型变量的。但是,这些通常需要您管理创建和销毁变量的整个过程,我不确定您是否可以将它们与__thread
结合使用。
pthread_cleanup_push
不适合。它旨在允许在线程在使用资源的(短(代码块期间退出时释放资源;如您链接到的文档中所述,它必须与该函数相同级别的pthread_cleanup_pop
匹配,如果线程从其 main 函数返回,则不会调用处理程序。这意味着,如果您希望线程局部变量在调用函数之间持久存在,则不能使用它。
对于那些不禁止使用第三方库的人来说,Boost 提供了一种方便、可移植的方式来管理线程本地存储。
正如迈克所说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持续时间空间 - 在给定线程的第一个条目上,我们使用放置 new 来构造存储中的变量
- 我们还
__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();
}
笔记:
- 您需要为每个 pthread_* 调用添加错误检查。 我只是删除了它进行阐述。
- 它使用
__thread
这是一个GNU扩展 - 它使用表达式语句将辅助__thread变量名称排除在父范围之外。 这也是一个GNU扩展。
- 在std::thread中,joinable()然后join()线程安全吗
- 对于MacOS上的G++,如何添加默认的include目录/usr/local/include和默认的库搜索路径/usr
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- 分离一个静态常量 std::thread?
- 尝试使用 std::vector<std::thread时出现静态断言失败错误>
- 使用 thread 类在 C++ 中构造线程的动态数组时出错
- 当指向对象的指针作为参数传递给 std::thread 时,内存可见性
- 如何从 std::thread 返回值
- 在C++中使用并行化的预期速度是多少(不是 OpenMp,而是 <thread>)
- "local scope"中的 C++ 初始化静态变量
- 将 std::thread by 值推送到列表中
- 转发变量参数列表以模拟 std::thread
- 嵌入式设备 -> std::thread -> FreeRTOS?
- 对 'std::thread::_M_start_thread CMake 的未定义引用进行基准测试
- 在 /usr/local/lib 下找不到库
- std::thread 增加 DLL 引用计数,从而防止卸载 DLL
- C++编译器错误"cannot be thread-local because it has non-POD type""
- 消除警告"construction of local static object is not thread-safe"
- TLS 变量上的"illegal thread-local reference to regular symbol"错误
- ActiveMQ-CPP 在发送时抛出"Thread local storage limit"达到