我的问题是,我们仍然需要将这些计数器声明为volatile,以便编译器在进行优化时不会使其松动.
或者编译器考虑到计数器被互斥体保护.
我明白互斥是用于同步的运行时机制,“volatile”关键字是一个编译时指示,编译器在进行优化时做正确的操作.
问候,
-Jay.
解决方法
>挥发性
线程,锁,内存障碍等
volatile用于告诉编译器生成代码以从内存中读取变量,而不是从寄存器读取变量.并且不要重新排序代码.一般来说,不优化或采取“快捷”.
内存障碍(由互斥锁,锁等提供)在Herb Sutter引用另一个答案中,是为了防止cpu重新排序读/写存储器请求,而不管编译器如何做.即不要优化,不要采取快捷方式 – 在cpu级别.
类似的,但实际上是非常不同的事情.
在你的情况下,在大多数锁定情况下,volatile不是必需的,这是因为为了锁定而进行的函数调用.即:
external void library_func(); // from some external library global int x; int f() { x = 2; library_func(); return x; // x is reloaded because it may have changed }
除非编译器可以检查library_func()并确定它不触及x,否则它将在返回时重新读取x.这甚至没有波动.
线程:
int f(SomeObject & obj) { int temp1; int temp2; int temp3; int temp1 = obj.x; lock(obj.mutex); // really should use RAII temp2 = obj.x; temp3 = obj.x; unlock(obj.mutex); return temp; }
在读取obj.x for temp1之后,编译器将重新读取temp2的obj.x – 不是因为锁的魔力 – 而是因为不确定lock()是否被修改为obj.您可以设置编译器标志来积极地优化(无别名等),因此不会重新读取x,但是一堆代码可能会开始失败.
对于temp3,编译器(希望)不会重新读取obj.x.
如果由于某种原因,obj.x可能在temp2和temp3之间改变,那么您将使用volatile(并且您的锁定将被破坏/无用).
最后,如果你的lock()/ unlock()函数以某种方式内联,也许编译器可以评估代码,看到obj.x没有被改变.但是我保证这里有两件事情之一:
– 内联代码最终会调用一些操作系统级锁定功能(从而防止评估)或
– 您调用一些asm内存屏障指令(即包装在__InterlockedCompareExchange中的内联函数),您的编译器将会识别,从而避免重新排序.
编辑:我忘了提到 – 对于pthreads的东西,一些编译器被标记为“POSIX兼容”,这意味着,除了别的以外,它们将会识别pthread_函数,而不会对它们进行不好的优化.即使C标准尚未提到线程,那些编译器(至少最低限度)也是这样.
所以,简短的回答
你不需要挥发性.