C++ 中良性竞争条件的保证

ano*_*ous 3 c++ multithreading race-condition

我知道 C++ 标准不能保证存在数据争用的任何情况(我相信数据争用具有未定义的行为,这意味着任何事情都会发生,包括程序终止、修改随机内存等...)。

是否有任何架构,其中由一个线程写入内存位置和一个线程从同一位置读取(不同步)组成的数据竞争不会导致读取操作读取未定义的值,并且内存位置“最终”(在内存屏障之后)更新为写入操作写入的值?

[编辑以将“竞争条件”替换为“数据竞争”]

nos*_*sid 6

数据争用的问题不在于您可以在机器级别读取错误的值。数据竞争的问题在于,编译器和处理器都对代码执行了大量优化。为了确保这些优化在存在多个线程的情况下是正确的,它们需要有关可以在线程之间共享的变量的附加信息。此类优化可以例如:

  • 重新排序操作
  • 添加额外的加载和存储操作
  • 删除加载和存储操作

Hans Boehm有一篇关于良性数据竞争的好论文,名为“如何使用“良性”数据竞争错误编译程序”。以下摘录自该论文:

双重检查延迟初始化

众所周知,这在源代码级别是不正确的。典型的用例看起来像

if (!init_flag) {
    lock();
    if (!init_flag) {
        my_data = ...;
        init_flag = true;
    }
    unlock();
}
tmp = my_data;
Run Code Online (Sandbox Code Playgroud)

my_data没有什么可以阻止优化编译器使用 of的设置重新排序 ,或者甚至 在第一次测试之前init_flag将 的加载提前到,在条件 if 未设置时重新加载它。即使编译器不执行任何转换,某些非 x86 硬件也可以执行类似的重新排序。其中任何一个都可能导致最终读取 看到未初始化的值并产生不正确的结果。my_datainit_flaginit_flagmy_data


这是另一个例子,其中int x是共享变量,int r是局部变量。

int r = x;
if (r == 0)
    printf("foo\n");
if (r != 0)
    printf("bar\n");
Run Code Online (Sandbox Code Playgroud)

如果我们只是说,读取x会导致未定义的值,那么程序将打印“foo”或“bar”。但是,如果编译器按如下方式转换代码,则程序也可能打印两个字符串或不打印任何字符串。

if (x == 0)
    printf("foo\n");
if (x != 0)
    printf("bar\n");
Run Code Online (Sandbox Code Playgroud)