为什么这可能导致僵局

Why this may lead to a deadlock?

本文关键字:僵局 为什么      更新时间:2023-10-16

请参阅以下从http://www.open-std.org/jtc1/sc22/wg21/wg21/docs/papers/2015/n4507.pdf带来的代码。/p>

using namespace std::experimental::parallel;
std::atomic<int> x = 0;
int a[] = {1,2};
for_each(par, std::begin(a), std::end(a), [&](int n) {
  x.fetch_add(1, std::memory_order_relaxed);
  // spin wait for another iteration to change the value of x
  while (x.load(std::memory_order_relaxed) == 1) { }
});

此示例中附带了一个评论:

上面的示例取决于迭代执行顺序, 因此是不确定的(可能僵局)。

,但我看不出为什么会导致僵局。正如我所理解的,尽管内存顺序被指定为std::memory_order_relaxed,但这仅是关于线程的时机看到另一个线程的某些更改,因此最终(在可能无限的时间之后),读数线程应注明更改。

有人可以解释吗?谢谢!

示例代码可以死锁与使用memory_order_relaxed无关的原因。

对原子变量的更改将对另一个核心可见,如果它不明显(根据标准,IT 应该可见),它与内存订购无关,后者仅用于指定与原子操作相对于原子操作的其他内存操作。

文档中给出的示例链接指的是可以死锁的,因为显然不能保证执行是真正的并发。在后来的草稿(N4640)中,文本已进行了修订:

...取决于迭代的执行顺序,如果两个迭代均在同一执行线程上执行。

这就是全部;如果两个迭代均顺序执行,则第一个不断旋转的值永远不会更改。

线程在等待时正在读取新值,因此在某个时候,他们应该看到他们所做的更改。

不,不是。

放松的内存顺序并不意味着"在此阅读之前,最终可以订购其他线程的内容"。这意味着"在读取此内容之前,没有从其他线程中订购事物"。也就是说,任何其他写作都可能永远不会看到。

因此ub。

现在,在实用系统上,放松的记忆顺序(尤其是对于无锁的原子学)完全有可能使其他线程的修改可见。但是就标准而言,这就是UB。而且,一个过度狂热的编译器确实可以将您的代码编译成硬while(true);,从不费心从变量中读取,因为它知道它看不到其他线程的变化。

x.fetch_add

使原子量增加,但

std::memory_order_relaxed

不是线程的同步点,因此任何两个线程都可以同时具有1个。因为读取模式 - 写入不同步而开始,因此没有所需的x值。