多线程中是否需要原子类型?(OS X,Clang,C ++ 11)

永无止境

我试图证明不使用std::atomic<>s是一个非常糟糕的主意,但是我无法创建一个再现失败的示例。我有两个线程,其中一个可以:

{
    foobar = false;
}

和另一个:

{
    if (foobar) {
        // ...
    }
}

类型foobar或者是bool或者std::atomic_bool和它的初始化true我正在使用OS X Yosemite,甚至尝试使用技巧通过CPU相似性来暗示我希望线程在不同的内核上运行。我在循环等中运行此类操作,无论如何,执行之间没有明显的区别。我最终用clang检查了生成的程序集,clang -std=c++11 -lstdc++ -O3 -S test.cpp然后看到读取的asm差异很小(左侧没有原子,右侧是原子):

在此处输入图片说明

mfence或“戏剧性”的东西。在写方面,发生了更多“戏剧性”的事情:

在此处输入图片说明

如您所见,该atomic<>版本xchgb使用了隐式锁。当我使用相对旧版本的gcc(v4.5.2)进行编译时,我可以看到mfence添加了各种s,这也表明存在严重的顾虑。

我有点理解“ X86实现了一个非常强大的内存模型”(ref),这mfence也许不是必需的,但这是否意味着除非我要编写支持ARM的跨平台代码,否则我真的不需要放任何atomic<>s,除非我关心ns级的一致性?

我看过Herb Sutter的“ atomic <>武器”,但仍然印象深刻的是,创建一个简单的示例来再现这些问题是多么困难。

塞巴斯蒂安·雷德尔(Sebastian Redl)

数据争用的一个大问题是它们是未定义的行为,不能保证是错误的行为。而且,这与线程的一般不可预测性以及x64内存模型的强度相结合,意味着创建可重现的故障真的很难。

优化器执行意外的操作时,故障模式会更加可靠,因为您可以观察到装配中的故障。当然,该优化器也非常出色,如果仅更改一条代码行,它的功能可能会完全不同。

这是一次在代码中出现的示例失败。该代码实现了某种自旋锁,但未使用原子。

bool operation_done;
void thread1() {
  while (!operation_done) {
    sleep();
  }
  // do something that depends on operation being done
}
void thread2() {
  // do the operation
  operation_done = true;
}

这在调试模式下工作正常,但是发行版本卡住了。调试显示线程1的执行从未离开循环,而查看程序集,我们发现条件消失了;循环就是无限的。

问题在于,优化器意识到在其内存模型下,operation_done不可能在循环内进行更改(这将是一场数据竞赛),因此“知道”一旦条件为真,那么它将永远永远如此。

将operation_done的类型更改为atomic_bool(或实际上是C ++ 11之前的编译器特定的等效项)可以解决此问题。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章