为什么thread_local不能应用于非静态数据成员,以及如何实现线程本地非静态数据成员
Why may thread_local not be applied to non-static data members and how to implement thread-local non-static data members?
为什么thread_local
不能应用于非静态数据成员?对于这个问题,公认的答案是:"将非静态结构或类成员设置为线程局部是没有意义的。"老实说,我看到了很多让非静态数据成员线程局部化的好理由。
假设我们有某种ComputeEngine
,其成员函数computeSomething
被连续调用多次。成员函数内部的一些工作可以并行完成。为此,每个线程都需要某种ComputeHelper
来提供,例如,辅助数据结构。所以我们实际需要的是:
class ComputeEngine {
public:
int computeSomething(Args args) {
int sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < MAX; ++i) {
// ...
helper.xxx();
// ...
}
return sum;
}
private:
thread_local ComputeHelper helper;
};
不幸的是,这段代码无法编译。我们可以这样做:
class ComputeEngine {
public:
int computeSomething(Args args) {
int sum = 0;
#pragma omp parallel
{
ComputeHelper helper;
#pragma omp for reduction(+:sum)
for (int i = 0; i < MAX; ++i) {
// ...
helper.xxx();
// ...
}
}
return sum;
}
};
然而,这将在computeSomething
的连续调用之间构造和销毁ComputeHelper
。假设构造ComputeHelper
是昂贵的(例如,由于巨大向量的分配和初始化),我们可能希望在连续调用之间重用ComputeHelper
。这让我想到了下面的模板方法:
class ComputeEngine {
struct ThreadLocalStorage {
ComputeHelper helper;
};
public:
int computeSomething(Args args) {
int sum = 0;
#pragma omp parallel
{
ComputeHelper &helper = tls[omp_get_thread_num()].helper;
#pragma omp for reduction(+:sum)
for (int i = 0; i < MAX; ++i) {
// ...
helper.xxx();
// ...
}
}
return sum;
}
private:
std::vector<ThreadLocalStorage> tls;
};
- 为什么
thread_local
不能用于非静态数据成员?什么这一限制背后的理由是什么?难道我没有给你一个好机会吗在这个例子中,线程本地的非静态数据成员非常完美理解吗? 实现线程局部非静态的最佳实践是什么数据成员吗?
至于为什么thread_local
不能应用于非静态数据成员,它会破坏这些成员通常的排序保证。也就是说,单个public/private/protected
组中的数据成员必须按照与类声明相同的顺序在内存中布局。更不用说如果你在堆栈上分配一个类会发生什么——TLS成员不会进入堆栈。
至于如何解决这个问题,我建议使用boost::thread_specific_ptr
。你可以把其中一个放到你的类中,得到你想要的行为。
线程本地存储的工作方式通常是在线程特定的数据结构(例如Windows中的TEB)中获得一个指针
只要所有线程局部变量都是静态的,编译器就可以很容易地计算出这些字段的大小,分配相应大小的结构体,并在该结构体中为每个字段分配一个静态偏移量。
一旦允许非静态字段,整个方案就会变得更加复杂——解决这个问题的一种方法是在每个类中增加一个间接层并存储一个索引(现在你在类中有了隐藏字段,这是意想不到的)。
他们显然决定让每个应用程序根据需要来处理它,而不是将这种方案的复杂性提升到实现者身上。
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 静态数据成员模板专用化的实例化点在哪里
- 调用在 HXX 文件中声明的静态数据成员
- 虚拟成员函数的定义是否强制在同一转换单元中动态初始化静态数据成员?
- 错误: 无效使用非静态数据成员"应用程序::应用程序构造函数"
- 静态数据成员:它"const declaration / constexpr definition"起作用?
- 何时需要定义类的静态数据成员 (un/-)
- 为什么静态数据成员不能在c++11中的类中初始化
- 静态数据成员的模板专用化
- 拒绝包含某些公共静态数据成员的类型
- GCC:在调试构建中优化的静态数据成员
- 类模板静态数据成员定义/声明/初始化
- C++ 中的静态数据成员
- 如果类在 C++ 中具有常量或引用类型的非静态数据成员,为什么编译器不提供默认赋值运算符?
- 使用 lambda 函数初始化静态数据成员
- 引用静态数据成员
- 学习C++并在早期示例中遇到错误(在非静态数据成员之前需要构造函数)
- Windows 上的 Clang/LLVM 7 和 8 多次初始化内联静态数据成员(使用 link.exe 和 lld-
- 为什么不能使用"( )"为类的非静态数据成员提供默认值?
- 不带初始值设定项的 constexpr 静态数据成员