在C++ 11中,您可以使用thread_local存储创建一个非平凡的对象:
class X { ... }
void f()
{
thread_local X x = ...;
...
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这个功能尚未在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 …Run Code Online (Sandbox Code Playgroud) 我有使用 thread_local 缓冲区的代码,类似于:
int func() {
thread_local std::vector<int> buffer;
buffer.resize(0);
for (int i = 0; i < 10000; i++) {
buffer.push_back(i);
}
return processing(buffer);
}
Run Code Online (Sandbox Code Playgroud)
在分析我的代码时,我注意到 gcc 在循环体内调用了一个调用_tls_get_addr(),以便访问buffer. 循环体的Godbolt反汇编如下所示:
lea rbx, -20[rbp]
data16 lea rdi, f()::buffer@tlsgd[rip]
.value 0x6666
rex64
call __tls_get_addr@PLT ; <- This call!
mov rsi, rbx
mov rdi, rax
call std::vector<int, std::allocator<int> >::push_back(int const&)@PLT
Run Code Online (Sandbox Code Playgroud)
这些调用大大减慢了循环速度。我可以手动使用此版本并参考:
lea rbx, -20[rbp]
data16 lea rdi, f()::buffer@tlsgd[rip]
.value 0x6666
rex64
call __tls_get_addr@PLT ; <- This call!
mov …Run Code Online (Sandbox Code Playgroud) 我最近使用了一个对象,其目的是将内存作为单例进行分配和释放。就像是
class MyValue
{
// ...
static Allocator& GetAllocator()
{
static Allocator allocator;
return allocator;
}
// ...
};
Run Code Online (Sandbox Code Playgroud)
后来我意识到它Allocator不是线程安全的:当多个线程同时使用同一个分配器时,偶尔会发生奇怪的事情,导致断言和分段错误。
解决方案:为不同的线程使用不同的分配器:
class MyValue
{
// ...
static Allocator& GetAllocator()
{
thread_local static Allocator allocator;
return allocator;
}
// ...
};
Run Code Online (Sandbox Code Playgroud)
惊人的!我的问题都消失了!只有一个问题: 每次创建线程时我的分配器变量都会被初始化,即使大多数线程不会使用该变量?
分配器的初始化可能是繁重的操作,因此我希望仅在实际需要时才对其进行初始化,而不是在每个线程中进行初始化。
我读到thread_local变量是由每个线程分配的。这是否意味着它们也是被构建的?这种分配(或构造)是针对创建的每个线程系统地发生还是仅针对使用它的线程发生?
我依稀记得在课程中听说过有关线程和线程本地存储的大多数细节都依赖于平台。如果是这样的话,我对 Linux 和 FreeBSD 特别感兴趣。
相关(有趣的读物,但我在那里找不到答案):