使thread_local变量完全不稳定

era*_*ran 11 c++ multithreading volatile thread-local boost-context

我正在使用一个使用用户级上下文切换的运行时库(使用Boost :: Context),并且在使用thread_level变量时遇到了麻烦.考虑以下(简化)代码:

thread_local int* volatile tli;

int main()
{
    tli = new int(1);   // part 1, done by thread 1
    UserLevelContextSwitch();
    int li = *tli;      // part 2, done by thread 2
    cout << li;
}
Run Code Online (Sandbox Code Playgroud)

由于对thread_local变量有两次访问,因此编译器将main函数转换为沿着这些行的某些东西(与汇编相反):

register int** ptli = &tli; // cache address of thread_local variable
*ptli = new int(1);
UserLevelContextSwitch();
int li = **ptli;
cout << li;
Run Code Online (Sandbox Code Playgroud)

这似乎是一种合法的优化,因为volatile 的值tli不会缓存在寄存器中.但是, volatile 的地址tli实际上是缓存的,而不是从第2部分的内存中读取的.

这就是问题:在用户级上下文切换之后,执行第1部分的线程会转到其他位置.然后,第2部分由其他线程获取,该线程获得先前的堆栈和寄存器状态.但是现在正在执行第2部分的线程读取tli属于线程1的值.

我试图找出一种方法来阻止编译器缓存线程局部变量的地址,并且volatile不够深入.是否有任何技巧(最好是标准的,可能是GCC特定的)来阻止线程局部变量地址的缓存?

Ant*_*ton 7

无法将用户级上下文切换与TLS配对.即使使用原子和完整的内存栅栏,缓存地址似乎也是合法的优化,因为thread_local变量是文件范围的静态变量,它不能像编译器所假设的那样移动.(但是,也许一些编译器仍然可以对编译器内存障碍敏感,比如std::atomic_thread_fenceasm volatile ("" : : : "memory");)

当同一点之后不同的线程可以继续执行时,使用您描述的相同的技术来实现"持续窃取".他们明确不鼓励在Cilk计划中使用TLS.相反,他们建议使用"hyperobjects" - Cilk的一个特殊功能,它替代TLS(并且还提供串行/确定性连接语义).又见Cilk的开发呈现thread_local和并行.

此外,当使用Fibers(相同的轻量级上下文切换)时,Windows提供FLS(光纤本地存储)作为TLS替换.