C++ 在Windows+;视觉的?

C++ 在Windows+;视觉的?,c++,visual-c++,atomic,volatile,memory-fences,C++,Visual C++,Atomic,Volatile,Memory Fences,这个网站上有几个问题,询问是否可以使用volatile变量进行原子/多线程访问:例如,请参阅 现在,C(++)标准一致性答案显然是否 但是,在Windows和Visual C++编译器中,情况似乎并不十分清楚。 我最近引用了关于volatile 特定于Microsoft的 声明为volatile的对象是(…) 对易失性对象的写入(volatile write)具有释放语义; 写入之前发生的对全局或静态对象的引用 指令序列中的易失性对象将在此之前出现 易失性写入已编译的二进制文件 易失性对象的读

这个网站上有几个问题,询问是否可以使用
volatile
变量进行原子/多线程访问:例如,请参阅

现在,C(++)标准一致性答案显然是

但是,在Windows和Visual C++编译器中,情况似乎并不十分清楚。 我最近引用了关于volatile

特定于Microsoft的 声明为volatile的对象是(…)

  • 对易失性对象的写入(volatile write)具有释放语义; 写入之前发生的对全局或静态对象的引用 指令序列中的易失性对象将在此之前出现 易失性写入已编译的二进制文件
  • 易失性对象的读取(易失性读取)具有获取语义;提及 全局或静态对象,在读取系统中的易失性内存后发生 指示 序列将在编译的二进制文件中的易失性读取之后发生
这允许易失性对象用于多线程应用程序中的内存锁定和释放

[我的重点]

现在,在我看来,一个易失性变量将被MS编译器视为即将到来的C++11标准中的
std::atomic

然而,在一篇文章中,用户写道“那篇MSDN文章非常不幸,它完全错了。你不能用volatile实现锁,即使是微软的版本。(……)


请注意:MSDN中给出的示例似乎很可疑,因为没有原子交换通常无法实现锁。(同样)这仍然留下问题。这篇MSDN文章中给出的其他信息的有效性,特别是对于和之类的用例。)


此外,还有用于联锁*函数的文档,特别是带变量和原子读写的文档。(请注意,我们的一个问题----并没有权威性地回答只读或只读原子访问是否需要此函数。)

更重要的是,上面引用的
volatile
文档在某种程度上暗指了“全局或静态对象”,我认为“真实”应该适用于所有值

回到问题上来 < Windows >,用VisualC++(2005—2010),将声明一个(32位int)变量为“代码>易失性< /COD>允许对该变量进行原子读写,或者不写?< /P> 对我来说特别重要的是,它应该(或不)在Windows/VC++上独立于程序运行的处理器或平台。(也就是说,在安腾2上运行的是WinXP/32位还是Windows 2008R2/64位,这有关系吗?)


请用可验证的信息、链接、测试用例备份您的答案

是的,它们在windows/vc++上是原子的(假设您满足对齐要求等)

然而,对于锁,您需要一个原子测试和设置,或者比较和交换指令或类似的指令,而不仅仅是一个原子更新或读取

否则,就无法测试锁并在一次不可分割的操作中声明它


编辑:如下所述,32位或更低级别的x86上的所有对齐内存访问都是原子的。关键是volatile使内存访问有序。(感谢在评论中指出)

< P> Visual C++ 2005的易失性变量是原子的。但这只适用于这类特定的编译器和x86/AMD64平台。例如,PowerPC可能会对内存读/写进行重新排序,并且需要读/写屏障。我不熟悉gcc类编译器的语义,但在任何情况下,使用volatile for atomics都不是很好移植


参考资料,请参阅第一句话“特定于Microsoft”:

有点离题,但我们还是要试一试

。。。这里有Interlocated*函数的文档,特别是InterlocatedExchange,它接受一个volatile(!)变量

如果你想一想:

void foo(int volatile*);
它是否说:

  • 参数必须是指向易失性int的指针,或
  • 参数也可以是指向易失性int的指针
后者是正确答案,因为函数可以同时传递到volatile和non-volatile int的指针


因此,
InterlockedExchangeX()
的参数volatile是限定的,这并不意味着它必须只对volatile整数进行操作。

重点可能是允许这样的东西

singleton& get_instance()
{
    static volatile singleton* instance;
    static mutex instance_mutex;

    if (!instance)
    {
        raii_lock lock(instance_mutex);

        if (!instance) instance = new singleton;
    }

    return *instance;
}
如果在初始化完成之前写入
实例
,则会中断。有了MSVC语义,只要看到
instance!=0时,对象已完成初始化(如果没有适当的屏障语义,即使使用传统的volatile语义,情况也并非如此)

这种双重检查锁(反)模式实际上很常见,如果不提供屏障语义,它就会被破坏。但是,如果保证对
volatile
变量的访问是获取+释放障碍,那么它是有效的

不过,不要依赖于
volatile
的这种自定义语义。我怀疑这是为了不破坏现有的代码库而引入的。无论如何,不要根据MSDN示例编写锁。它可能不起作用(我怀疑你能否只用一个屏障来写一个锁:你需要原子操作——CAS、TA等等)


编写双重检查锁模式的唯一可移植方法是使用C++0x,它提供了合适的内存模型和显式屏障。

在x86下,这些操作保证是原子操作,而不需要基于锁的指令,如
联锁*
(请参阅英特尔开发人员手册3A第8.1节):

基本内存操作将始终以原子方式执行: