【问题标题】:C atomic operations, if writes are all atomic swaps do I need atomic load?C原子操作,如果写入都是原子交换,我需要原子加载吗?
【发布时间】:2013-02-08 20:45:06
【问题描述】:

我正在用 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)足以防止这种情况发生......但我不确定。

【问题讨论】:

    标签: c multithreading atomic thread-synchronization compare-and-swap


    【解决方案1】:

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

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

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

    如果您只对确保从 shared_x 阅读不会让您产生垃圾并且不关心上述竞争条件感兴趣,那么答案是“很可能您不需要读原子。”您可以在这里阅读详细信息,而不是我复制和粘贴:

    Atomicity in C++ : Myth or Reality

    尽管问题是针对 C++ 的,但对于 C 也是如此。

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

    【讨论】:

    • 更新了我的示例,现在它显示了在计算新值的同时写入其他内容时会发生什么,它只是重新开始并再次尝试。
    • @Exodist 这里需要的视角是整个程序,而不仅仅是其中的一部分。您需要自己进行分析。假设一个竞争条件并遵循程序逻辑。如果比赛条件根本没有任何不良影响,那你就没事了。您也可以手动引入竞态条件并进行观察。
    • 我一定没有正确地问我的问题。我不关心工作的同步。我将在上面更新我的问题。
    • 已更新,我想确保 int x 不会获得旧 shared_x 的一半值和新 shared_x 的一半值。
    • @Exodist 好的。通过链接到看起来与您的问题重复的内容来更新答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-25
    • 1970-01-01
    • 1970-01-01
    • 2015-01-10
    • 1970-01-01
    相关资源
    最近更新 更多