【问题标题】:Do I need to synchronize primitives in C++我需要在 C++ 中同步原语吗
【发布时间】:2012-12-26 08:05:35
【问题描述】:

我的班级中有一个 volatile bool 'play' 标志,该标志由一个线程设置并由另一个线程读取。

我是否需要将调用同步到该标志?例如在这个函数中:

void stop() 
{
   play = false;
}

在 Windows 中它们有 _InterlockedExchange 而 OSX 有 OSAtomicAdd64Barrier,我已经看到这些函数与共享原语一起使用,我需要它们吗?

谢谢

【问题讨论】:

  • 某些线程是否也读取该变量?
  • 是的,它正在被工作线程读取。

标签: c++ synchronization primitive concurrent-programming


【解决方案1】:

是的,volatile 绝不意味着线程安全或原子。如果可以,请使用std::mutexstd::unique_lock 等,而不是平台细节。 std::atomic 是不错的选择——在这种情况下可能是最好的选择。

【讨论】:

  • 你确定吗?想说明在这种特定情况下如何发生数据竞争?
  • 除非您在读取值的线程中的循环中检查play 的值,否则您将在某些时候错过更新,除非您以某种方式对其进行同步。 volatile 几乎与它所暗示的原子性正交。请参阅此处以获取另一个答案:stackoverflow.com/questions/8819095/…
  • 我明白什么是 volatile,但假设主线程没有读取play,并且工作人员没有写入play(这是我从问题中理解的) - 我看不到如何可能会发生竞争条件(尽管我想不出一种方法来证明它也不会发生)。你说它仍然不安全 -> 可能会发生数据竞争,我正在寻找能证明你的答案是正确的杀手案例。
  • 如果您不关心确切地知道标志何时更新,那么您不必使用原子/同步。但是,该标准不保证其他线程何时会看到更新的值,除非它会在某个时候看到。在这里它可能并不重要。这实际上取决于您的读者线程正在/正在做什么。
  • @amit: [intro.multithread]/4: “如果其中一个修改内存位置 (1.7) 而另一个访问或修改相同的内存位置,则两个表达式计算冲突。”和[intro.multithread]/21:“如果程序的执行包含不同线程中的两个冲突操作,则程序的执行包含数据竞争,其中至少一个不是原子的,并且两者都不会在另一个之前发生。任何此类数据竞争都会导致未定义的行为。 "。
【解决方案2】:

取决于:

如果您使用的是具有总存储顺序内存模型(例如 x86 / x64)的 CPU 或任何只有 1 个 CPU 内核的机器,那么您的问题的答案是否定的,因为您声明只有 1 个线程写入标志和屏障指令可能在 x86 上无论如何都被优化掉了。如果您在具有宽松内存模型的 CPU 上编译相同的代码,那么情况会发生变化,然后您可能会发现在 x86 上完美运行的代码如果您在 ARM 上编译和运行它会产生一些奇怪且难以重现的错误或以 PPC 为例

volatile 指令防止写入被缓存在某处,这可能意味着读取线程比volatile 指令不存在时更快地看到写入。是否要使用这个取决于这个间隔有多重要

【讨论】:

    【解决方案3】:

    这取决于线程之间是否有任何其他数据共享。线程的问题是,一个线程可能会以与最初创建它们的线程不同的顺序看到来自不同线程的写入。在这种情况下,您需要使用某种 _Interlocked*/Atomic 函数或锁(在两个线程中),他们保证在标志之前所做的所有更改对另一个线程可见。

    如果没有其他共享数据(或只有只读共享数据),或者您在 x86 上运行,则仅使用 volatile 也应该可以。然而,它的工作在某种意义上只是偶然的,并且没有任何标准保证,所以如果你的平台支持它,仍然建议使用某种形式的 Atomic/interlocked/etc。将来(即一旦有良好的编译器支持)您应该使用 C++11 的std::atomcic,因为它可以在平台之间移植。

    您不应该使用裸非易失性变量,否则编译器可能会决定优化检查。 volatile 与 camelccc 建议的缓存关系不大。

    【讨论】:

    • 如果只有 1 个线程在写,他不需要原子变量,如果性能很重要,可能有很好的理由不使用。如果您想在非 x86 CPU 上进行移植,您可能需要在写入线程中设置内存屏障,并且 volatile 保证在 Microsoft 编译器(不管 CPU)上暗示这一点,尽管不是 gcc。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多