原子指令和变量更新可见性

时间:2009-10-23 16:53:22

标签: multithreading thread-safety atomic race-condition memory-fences

在大多数常见平台上(最重要的是x86;我明白有些平台有极其困难的内存模型,几乎没有保证对多线程有用,但我不关心罕见的反例),是以下代码安全

主题1:

someVariable = doStuff();
atomicSet(stuffDoneFlag, 1);

主题2:

while(!atomicRead(stuffDoneFlag)) {}  // Wait for stuffDoneFlag to be set.
doMoreStuff(someVariable);

假设原子操作的标准合理实现:

  1. 线程1对someVariable的分配是否保证在调用atomicSet()之前完成?
  2. 线程2保证在调用someVariable之前看到doMoreStuff()的作业,前提是它原子地读取stuffDoneFlag吗?
  3. 编辑:

    1. 我正在使用的原子操作的实现在每个中包含x86 LOCK指令 操作,如果有帮助。
    2. 假设stuffDoneFlag以某种方式正确清除。怎么不重要。
    3. 这是一个非常简化的例子。我以这种方式创建它,这样你就不必理解问题的整个上下文来回答它。我知道它效率不高。

5 个答案:

答案 0 :(得分:2)

如果您的实际x86代码在Thread 1中的atomicSet中存储到someVariable,并且在Thread 2中的atomicRead中加载之后加载someVariable,那么您应该没问题。 Intel's Software Developer's Manual Volume 3A在8.2节中指定了x86的内存模型,这里的线程内存储和负载加载约束就足够了。

但是,可能没有任何东西阻止您的编译器重新排序从您在原子操作中使用的任何更高级语言生成的指令。

答案 1 :(得分:0)

1)是

2)是

两者都有效。

答案 2 :(得分:0)

这段代码看起来是线程安全的,但我质疑你的spinlock(while循环)的效率,除非你只是在很短的时间内旋转。在任何给定的系统上都无法保证线程2不会完全占用所有处理时间。

我建议使用一些实际的同步原语(看起来像boost::condition_variable就是你想要的)而不是依赖于自旋锁。

答案 3 :(得分:0)

原子指令确保线程2在线程2继续之前等待线程1完成设置变量。但是,有两个关键问题:

1) someVariable 必须声明为“volatile”,以确保编译器不会优化它的分配,例如将其存储在寄存器中或推迟写入。

2)第二个线程在等待信号时阻塞(称为spinlocking)。您的平台可能提供了更好的锁定和信令主要和机制,但相对简单的改进只是在线程2的sleep()主体中while()

答案 4 :(得分:0)

dsimcha写道:“假设stuffDoneFlag以某种方式被正确清除。多么重要。” 这不是真的!

让我们看看情景:

  1. Thread2检查stuffDoneFlag是否为1开始读取someVariable。
  2. 在Thread2完成读取任务调度程序之前,中断其任务并暂停该任务一段时间。
  3. Thread1再次访问someVariable并更改内存内容。
  4. 任务调度程序再次打开Thread2并继续执行该作业,但someVariable的内存内容已更改!