【发布时间】:2011-10-23 20:36:03
【问题描述】:
本网站上有几个问题询问是否可以使用volatile 变量进行原子/多线程访问:例如,请参阅here、here、or here。
现在,符合 C(++) 标准的答案显然是否。
但是,在 Windows 和 Visual C++ 编译器上,情况似乎不太清楚。
我最近answered 并在volatile 上引用了official MSDN docs
微软特定
声明为 volatile 的对象是 (...)
- 对 volatile 对象的写入(volatile write)具有 Release 语义; 对 全局或静态对象? 的引用发生在写入之前 指令序列中的易失性对象将在此之前发生 volatile 写入编译后的二进制文件。
- 对易失性对象的读取(易失性读取)具有 Acquire 语义;一个参考 全局或静态对象?在读取易失性内存后发生 操作说明 序列将在编译后的二进制文件中进行易失性读取之后发生。
这允许 volatile 对象用于多线程应用程序中的内存锁定和释放。
[强调我的]
现在,阅读本文,在我看来,一个 volatile 变量将被 MS 编译器视为 std::atomic 将在即将到来的 C++11 标准中。
但是,在comment to my answer 中,用户Hans Passant 写道“那篇MSDN 文章很不幸,它是大错特错。你不能用volatile 实现锁,即使是微软的版本。(。 ..)"
请注意:MSDN 中给出的示例 看起来很可疑,因为您通常无法在没有原子exchange 的情况下实现锁。 (同样pointed out by Alex。)这仍然留下了问题。这篇 MSDN 文章中给出的其他信息的有效性,尤其是像 here 和 here 这样的用例。)
此外,还有 The Interlocked* 函数的文档,尤其是 InterlockedExchange 使用 volatile(!?) 变量并执行原子读+写。 (请注意,我们对 SO 提出的一个问题——When should InterlockedExchange be used?——并没有权威地回答只读或只写原子访问是否需要此函数。)
更重要的是,上面引用的volatile 文档以某种方式暗示了“全局或静态对象”,我认为“真实”acquire/release semantics 应该适用于所有值。
回到问题
在 Windows 上,使用 Visual C++ (2005 - 2010),将(32 位?int?)变量声明为 volatile 允许对该变量进行原子读取和写入 - 或不允许?
对我来说特别重要的是,这应该(或不)在 Windows/VC++ 上保持(或不)独立程序运行的处理器或平台。 (也就是说,在 Itanum2 上运行的是 WinXP/32bit 还是 Windows 2008R2/64bit?)
请用可验证的信息、链接、测试用例备份您的答案!
【问题讨论】:
-
屏障语义并不意味着指令序列的原子性。特别是序列加载、添加、存储不是原子的。
-
@Alex:问题不在于序列,而在于单次读取或写入。
-
@Martin:即使在这种情况下,这也取决于硬件为您提供的功能。 VS 不会在
volatile访问周围添加lock,这意味着只有当底层处理器指令是原子的时它才会是原子的。 IE。例如,它不会用于未对齐的整数类型,也不会用于 32 位平台中的 64 位整数。 -
@Martin:在 32 位 Windows 操作系统中,使用
uint32_tvolatile引用,访问不是原子的。请注意,您引用的 MSDN 部分并没有说它保证操作的原子性,它只是说它提供的语义可能足以用于内存锁。我希望我有更多的时间来写一个完整的答案。 -
嗯...我刚刚注意到我的最后一条评论是错误的...变量将是
uint64_t,因为操作不是原子的。 Intel/AMD保证32bit模式下32bit读写原子性,64bit模式下64bit读写原子性。
标签: c++ visual-c++ atomic volatile memory-fences