|
|
1
15
这里有两个基本上不相关的项目,总是混淆。
volatile用来告诉 编译程序 产生从存储器而不是从寄存器中读取变量的代码。不要重新排列代码。一般来说,不优化或采取“捷径”。 记忆屏障(由互斥体、锁等提供),如另一个答案中所引用的Herb Sutter,用于防止 中央处理器 从重新排序读/写内存请求,不管编译器如何说。也就是说,不要优化,不要走捷径——在CPU级别。 相似,但事实上却截然不同。 在您的情况下,在大多数锁定情况下,不需要volatile的原因是 函数调用 是为了锁定而制造的。IE: 影响优化的普通函数调用:
除非编译器可以检查library_func()并确定它不接触x,否则它将在返回时重新读取x。这甚至没有挥发性。 线程:
在为temp1读取obj.x之后,编译器将为temp2重新读取obj.x,这不是因为锁的魔力,而是因为它不确定lock()是否修改了obj。您可能会将编译器标志设置为积极优化(没有别名等),因此不会重新读取x,但随后一堆代码可能会开始失败。 对于temp3,编译器(希望)不会重新读取obj.x。 如果由于某种原因obj.x可以在temp2和temp3之间更改,那么您将使用volatile(并且您的锁定将被破坏/无用)。 最后,如果您的lock()/unlock()函数以某种方式是内联的,那么编译器可能会对代码进行评估,并看到obj.x没有被更改。但我保证这里有两件事: -内联代码最终调用一些操作系统级锁函数(从而阻止计算),或者 -您可以调用一些ASM内存屏障指令(即封装在内联函数中的指令,如“interlockedcompareexchange”),编译器将识别这些指令,从而避免重新排序。 编辑:P.S.我忘了提到——对于pthread s的东西,一些编译器被标记为“posix兼容”,这意味着,除其他外,它们将识别pthread_u函数,并且不会对它们进行糟糕的优化。IE即使C++标准还没有提到线程,但这些编译器(至少是最低限度)。 所以,简短的回答你不需要反复无常。 |
|
|
2
13
从Herb Sutter的文章“使用关键部分(最好是锁)来消除种族”( http://www.ddj.com/cpp/201804238 )以下内容:
因此,当一个关键部分被输入和退出时(并且术语critical section在它的一般意义上被使用,不一定在win32意义上被
|
|
|
3
5
volatile用于通知优化器始终加载位置的当前值,而不是将其加载到寄存器中并假定它不会更改。当使用双端口内存位置或可以从线程外部的源实时更新的位置时,这是最有价值的。 互斥体是一种运行时操作系统机制,编译器实际上对它一无所知,所以优化器不会考虑到这一点。它将阻止多个线程一次访问计数器,但这些计数器的值仍然会发生更改,即使互斥体有效。 所以,您将var标记为volatile是因为它们可以从外部修改,而不是因为它们在互斥保护中。 保持不稳定。 |
|
|
4
4
虽然这可能取决于您使用的线程库,但我的理解是任何一个合适的库都将
不
需要使用
在Pthreads, for example ,使用互斥将确保数据正确地提交到内存中。 编辑: 我在此背书 tony's answer 比我自己的强。 |
|
|
5
3
您仍然需要“volatile”关键字。 互斥体阻止计数器并发访问。 “volatile”告诉编译器实际使用计数器 而不是将其缓存到CPU寄存器中 由并发线程更新)。 |
|
|
user107586 · 如何处理等待句柄不会导致无限循环? 1 年前 |
|
|
ron burgundy · 获取-释放语义是否跨线程传递?[副本] 1 年前 |
|
|
BenjiFB · C#内存缓存:在一次操作中追加到列表? 1 年前 |
|
|
András Takács · Python多线程问题 1 年前 |
|
|
András Takács · Python多线程错误 1 年前 |