如果我有这样的话......
volatile long something_global = 0;
long some_public_func()
{
return something_global++;
}
当使用多个线程访问时,期望此代码不会中断(竞争条件)是否合理?如果它不是标准的,它仍然可以作为现代编译器的合理假设吗?
注意:我使用的所有内容都是原子增量和减量 - 没什么比这更好的了。
答案 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或编译器内置函数(GCC,MSVC)的库。