使thread_local变量完全不稳定

时间:2014-09-04 19:43:34

标签: 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;
}

由于对thread_local变量有两次访问,编译器会将main函数转换为某些行(从汇编中反转):

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

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

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

我试图想办法阻止编译器缓存线程局部变量的地址,而volatile没有去足够深。是否有任何技巧(最好是标准的,可能是GCC特定的)来阻止线程局部变量的缓存。地址?

1 个答案:

答案 0 :(得分:7)

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

正如您所描述的那样,

使用the same technique来实现“继续窃取”,当不同的线程可以在同步点之后继续执行时。他们在Cilk程序中explicitly discourage使用TLS。相反,他们建议使用“hyperobjects” - Cilk的一个特殊功能,它替代TLS(并且还提供串行/确定性连接语义)。另请参阅Cilk开发人员presentation关于thread_local和并行性。

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