【发布时间】:2014-09-04 19:43:34
【问题描述】:
我正在开发一个使用用户级上下文切换(使用 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 变量的访问有两次,因此编译器将主函数转换为以下内容(从汇编反转):
register int** ptli = &tli; // cache address of thread_local variable
*ptli = new int(1);
UserLevelContextSwitch();
int li = **ptli;
cout << li;
这似乎是一个合法的优化,因为值 volatile tli 没有被缓存在寄存器中。但是地址 volatile tli 实际上被缓存了,而不是在第 2 部分从内存中读取。
这就是问题所在:在用户级上下文切换之后,执行第 1 部分的线程转到了其他地方。第 2 部分然后被其他线程拾取,该线程获取先前的堆栈并注册状态。但是现在正在执行第 2 部分的线程读取属于线程 1 的 tli 的值。
我试图找出一种方法来防止编译器缓存线程局部变量的地址,而volatile 的深度不够。是否有任何技巧(最好是标准的,可能是 GCC 特定的)来防止缓存线程局部变量的地址?
【问题讨论】:
-
我这里可能很密集,但是
thread_local你自己做线程有什么好处呢? -
@Mgetz,线程局部变量不是本质上是原子的吗?...如果编译器对原子过于保守,您的建议可能会起作用,但它不应该真的有缓存问题原子变量的地址与非原子变量一样。
-
C++11 的线程模型假定执行线程将从函数的顶部开始,然后遵循正常的调用顺序——它不支持在函数中间“开始”的线程函数(C++11 1.10/1“多线程执行和数据竞争”)。这并不是说您将无法解决您的问题,但我怀疑是否会有标准的方法来解决问题。不过,这个问题很好。
-
@500-InternalServerError,我的库管理任务,可以从 OS 线程迁移到 OS 线程。我需要一种方法来为每个正在运行的任务提供一些自己的身份信息。当任务映射到线程时,我管理身份的设置,但由于这个问题,身份的东西可能被映射到错误的线程。将身份信息作为每个调用的参数移动是不可取的,因为这会影响用户代码。
-
@eran:我理解你的问题,但是线程本地的实际 value 不会跟随硬件线程(因为没有更好的术语)?跨度>
标签: c++ multithreading volatile thread-local boost-context