1

在最常见的平台上(最重要的是 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

编辑:

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

5 回答 5

2

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

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

于 2009-10-30T02:45:05.497 回答
0

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

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

于 2009-10-23T18:11:13.273 回答
0

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

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

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

于 2009-10-23T18:20:01.553 回答
0

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

让我们看看场景:

  1. Thread2 检查 stuffDoneFlag 是否为 1 开始读取 someVariable。
  2. 在 Thread2 完成读取任务调度程序之前中断其任务并暂停该任务一段时间。
  3. Thread1 再次访问 someVariable 并更改内存内容。
  4. 任务调度程序再次打开 Thread2 并继续工作,但 someVariable 的内存内容已更改!
于 2009-10-23T19:08:56.210 回答
0

1)是的

2)是的

两者都有效。

于 2009-10-23T17:02:19.840 回答