gpt4 book ai didi

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

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:52:17 24 4
gpt4 key购买 nike

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

{
foobar = false;
}

和另一个:

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

foobar 的类型是 boolstd::atomic_bool并初始化为 true .我正在使用 OS X Yosemite,甚至尝试使用 this通过 CPU 亲和性暗示我希望线程在不同的内核上运行的技巧。我在循环等中运行这样的操作,在任何情况下,执行时都没有明显的差异。我最终用 clang clang -std=c++11 -lstdc++ -O3 -S test.cpp 检查生成的程序集我看到 read 上的 asm 差异很小(左边没有原子,右边有):

enter image description here

没有mfence或“戏剧性”的东西。在写入方面,发生了一些更“戏剧性”的事情:

enter image description here

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

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

我看过"atomic<> Weapons"来自 Herb Sutter,但我仍然对创建一个重现这些问题的简单示例有多么困难印象深刻。

最佳答案

数据竞争的最大问题是它们是未定义的行为,不能保证错误的行为。这一点,再加上线程的普遍不可预测性和 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;
}

这在 Debug模式下运行良好,但发布版本卡住了。调试发现thread1的执行一直没有离开循环,查看程序集发现条件没有了;循环简直是无限的。

问题是优化器意识到在它的内存模型下,operation_done 不可能在循环内改变(那将是数据竞争),因此它“知道”一旦条件一次为真,永远为真。

将 operation_done 的类型更改为 atomic_bool(或者实际上,C++11 之前编译器特定的等价物)解决了这个问题。

关于c++ - 多线程中是否需要原子类型? (OS X, clang, c++11),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39429056/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com