使用volatile作为原子

时间:2010-06-26 01:48:51

标签: c++ c multithreading concurrency atomic

如果我有这样的话......

volatile long something_global = 0;

long some_public_func()
{
    return something_global++;
}

当使用多个线程访问时,期望此代码不会中断(竞争条件)是否合理?如果它不是标准的,它仍然可以作为现代编译器的合理假设吗?

注意:我使用的所有内容都是原子增量和减量 - 没什么比这更好的了。

6 个答案:

答案 0 :(得分:16)

不 - 易失并不代表同步。它只是意味着每次访问都将返回最新的值(而不是在线程中本地缓存的副本)。

后增量不是原子操作,它是内存访问,后跟内存写入。交错两个意味着该值实际上只增加一次。

答案 1 :(得分:4)

不,您必须使用与平台相关的原子访问。有几个库可以抽象这些 - GLib提供可移植的原子操作,如果需要可以回退到互斥锁,我相信Boost也提供了可移植的原子。

作为recently learned,对于真正的原子访问,您需要一个volatile未提供的完整内存屏障。所有易失性保证是每次访问时都会重新读取内存,并且不会重新排序对volatile内存的访问。优化器可以在易失性读/写之前或之后重新排序某些非易失性访问 - 可能在增量中间! - 所以你必须使用实际的原子操作。

答案 2 :(得分:3)

在现代快速多核处理器上,由于缓存和写缓冲区,原子指令会产生很大的开销。

因此,编译器不会因为添加volatile关键字而发出原子指令。您需要求助于内联汇编或特定于编译器的扩展(例如gcc atomic builtins)。

我建议使用库。简单的方法是在想要更新变量时锁定。如果它们适合你正在做的事情,信号量可能会更快。似乎GLib提供了一个相当有效的实现。

答案 3 :(得分:3)

Windows提供InterlockedIncrement(以及InterlockedDecrement)来完成您的要求。

答案 4 :(得分:1)

Volatile只是阻止优化,但原子性需要更多。在x86中,指令必须以LOCK前缀开头,在MIPS中,RMW周期必须由LL / SC构造包围,...

答案 5 :(得分:0)

你的问题是C不保证增量运算符的原子性,实际上它们通常不是原子的。您必须使用类似Windows API或编译器内置函数(GCCMSVC)的库。