1

我正在用 C 语言编写一个程序。为了简单起见:有几个变量可供许多线程读写。每次写入其中一个时,它都会通过原子交换(GCC 原子操作、同步和交换)写入。每次读取其中一个变量时是否需要使用原子负载,或者原子写入是否足以避免在写入过程中读取数据?

请注意,任何需要使用这些变量之一的数据的地方首先复制该值:

int success = 0;
while ( !success ) {
  int x = shared_x;
  ... work with x, result in y ...
  success = cmp_swap( &shared_x, x, y );
}

我的问题不是关于数据竞赛,我不担心我可能会丢失数据。我担心 shared_x 的值可能会在我阅读的过程中发生变化。假设它是一个 8 字节整数,这是否是一个潜在的问题:假设 shared_x 是一个 64 位整数,8 字节。我的 x = shared_x 是否有可能复制前 4 个字节,然后以原子方式写入 shared_x,然后该语句完成读取第二个 4 个字节。这将导致 x 包含 shared_x 的旧值的前 4 个字节,以及新 shared_x 的最后 4 个字节。我怀疑原子交换中的内存屏障(http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html - 使用__sync_bool_compare_and_swap)足以防止这种情况......但我我不确定。

4

1 回答 1

3

看起来您正在阅读shared_x,计算新的东西,然后写回shared_x。您写入的值shared_x似乎取决于您最初从中读取的值。

如果是这种情况,您在那里有一个依赖项,并且很可能不仅需要使读取原子化,而且您需要使“读取、计算、回写”的整个操作原子化。意思是,你需要同步它。就像使用互斥锁一样。

我说“最有可能”是因为我不确定我是否不知道代码实际上做了什么。您需要分析在线程 A 写入shared_x而线程 B 当前正在根据旧值进行计算shared_x然后将结果写回它的竞争条件的情况下会发生什么。线程 A 写入它的值将永远丢失。我不知道这是否会给你带来问题。只有你能知道。如果该竞争条件正常,那么您不需要同步或使读取原子。

如果您只想确保读取 fromshared_x不会给您带来垃圾并且不关心上述竞争条件,那么答案是“很可能您不需要使读取原子化”。您可以在这里阅读详细信息,而不是我复制和粘贴:

C++ 中的原子性:神话还是现实

即使问题是针对 C++ 的,C 也同样如此。

请注意,原子现在也在 C 标准 (C11) 中,由<stdatomic.h>标头和_Atomic类型限定符提供。但当然不是所有的编译器都支持 C11。

于 2013-02-23T06:14:12.173 回答