c-C11内存模型——两个线程执行atomic_fetch_add,然后执行atomic_load——可能的输出是什么



考虑以下测试程序,该程序在完全实现C2011原子和线程的实现上编译并运行。

#include <stdio.h>
#include <stdatomic.h>
#include <threads.h>
#if !ATOMIC_INT_LOCK_FREE
#error "Program behavior is only interesting if atomic_int is lock-free."
#endif
static atomic_int var;
int inc_print(void *unused)
{
atomic_fetch_add(&var, 1);
printf(" %d", atomic_load(&var));
return 0;
}
int main(void)
{
thrd_t thrd;
if (thrd_create(&thrd, inc_print, 0) == thrd_success) {
inc_print(0);
thrd_join(&thrd, 0);
}
putchar('n');
return 0;
}

我设法说服自己,以下所有陈述都是真实的:

  1. 每个线程的atomic_load必须观察同一线程执行的增量,因此它不能读取零
  2. 每个线程的CCD_ 2可以观察到也可以不观察到由另一个线程执行的增量。(另一个线程可能在atomic_load之后才被调度。(因此,它可以读取1或2
  3. printf的调用仅针对彼此序列化。因此,如果一个线程的atomic_load读取1,而另一个线程atomic_load读取2,则可以打印1 22 1
  4. 两个atomic_load都有可能观察到另一个线程执行的增量,因此输出2 2也是可能的

但我不确定的是:atomic_loads的是否有可能观察到另一个线程执行的增量?也就是说,输出1 1可能吗?

此外,放松记忆模式会改变什么吗?

你的结论在我看来是正确的。

默认的memory_order_seq_cst保证整个程序以顺序一致的方式执行,因为它是无数据竞争的,并且不使用任何非SC原子。因此,可能的结果只是程序顺序的交错。

这允许两个增量,然后两个加载,但一个增量必须在另一个之后,查看其1结果并写入2。并且该线程中的负载必须在它之后,所以至少有一个线程看到21 1结果不可能,2 2结果可能发生。


宽松的原子论在这里没有引入任何新的可能性;我们可以从对相同原子变量的操作规则中获得相同的保证,这些规则无论memoryorder参数如何都适用。

  • var存在一致的修改顺序,总共两个原子增量必须等于+=2。(由于这个原因,原子RMW保证读取最新的值,以确保同一对象上的RMW彼此序列化,而不是同时加载0和写入1。这不会是原子增量。(
  • 同一线程中atomic_load1之后的load必须按照修改顺序查看其结果或稍后的某个值。(在C++中,这是写-读一致性保证,并且在(在同一线程中(强制同一原子对象上的两个操作之间排序之前进行排序。欢迎编辑,并提供C11标准中等效语言的链接。(

是的,printfs的有序性与var的原子修饰无关。它有效地锁定了stdout。如果这与C11mtx_lock的规则类似,那么这与互斥体上的acquire操作类似,因此它可以在增量或加载完成之前开始获取锁。

这并不相关;不要求CCD_ 27的输出是可能的。即使锁定是seq_cst,您也可以进入这样一种状态:两个printf都没有启动,但所有原子都已经完成。其中一个线程有一个1,另一个有一个2作为它们的临时线程。然后,这只是第一次打印的机会。

最新更新