C++ 为什么要将参考计数器值读取为参考到易失性常数?

C++ 为什么要将参考计数器值读取为参考到易失性常数?,c++,boost,C++,Boost,在Boost 1.5.1源代码中的smart\u ptr\detail\atomic\u count\u win32.hpp是一个整洁的小原子参考计数器Boost::detail::atomic\u count 在第48行,他们有一个我很好奇的演员阵容: class atomic_count { public: // ... operator long() const { return static_cast<long const volatile &>( val

在Boost 1.5.1源代码中的
smart\u ptr\detail\atomic\u count\u win32.hpp
是一个整洁的小原子参考计数器
Boost::detail::atomic\u count

在第48行,他们有一个我很好奇的演员阵容:

class atomic_count
{
public:

// ...

operator long() const
{
    return static_cast<long const volatile &>( value_ );
}

private:
long value_;
类原子计数
{
公众:
// ...
运算符long()常量
{
返回静态\u转换(值\u);
}
私人:
长期价值;

为什么计数器值强制转换为a-reference-to-a-volatile-constant-long(
long const volatile&
)?

在x86平台上,对于本机宽度的对齐值,这已经足够了

他们试图避免的问题是:

  • 该变量具有十六进制值
    0000FFFF

  • 线程A开始读取该值并获取
    0000xxxx
    部分

  • 线程B将值从
    0000FFFF
    增加到
    00010000

  • 线程A读取完值,得到它尚未读取的
    xxxx0000
    部分

  • 线程A现在已读取值
    00000000

  • 这被称为单词撕裂。然而,众所周知,x86上本机宽度的对齐类型不会发生这种情况。因此,只需通过转换
    volatile
    (已知可以避免出现问题的编译器优化)即可


    请注意,这不是一般的事实。这只是平台的一个属性。这不是可移植的代码。

    MSVC提供了一个现已弃用的,为它们提供了获取和释放语义(关于多线程编程的内存顺序保证)

    此强制转换“启用”变量上的此扩展,使其具有读取-获取语义(以匹配可能发生的任何发布-写入)。同样,这是非标准的。在C++11代码中,应使用
    std::atomic

    他们需要这样做是因为
    boost::shared_ptr
    保证了
    shared_ptr
    在多线程(共享)使用中的正确性;这是他们计数器的实现

    (此外,这只是故事的一半:虽然此扩展可能提供所需的顺序和可见性保证,但它不保证原子性。在Win32上,它运行的平台隐式地保证了这一点:对齐字大小的整数读取和写入在每个平台上都是原子的。)


    在它开始之前就把它扼杀在萌芽状态:没有这个扩展,
    volatile
    对于多线程编程是没有用的。甚至不要尝试。这个扩展已经被弃用了,所以如果可以的话,你应该真正避免它。

    值的类型是什么?@slavik262:它是一个常规的(不符合CV条件的)
    long
    。更让我印象深刻的是@Paul在这类事情上通常会放一个到代码的链接。我必须承认一件事,dave,我不理解“足够”。我的意思是,大家都知道不需要任何更具攻击性的东西(比如内联汇编或编译器内部版本)。(我用一些细节更新了答案。)原子性仅仅是需求的一半,您还需要解决排序和可见性问题。为此,您需要来自平台的隐式保证(如x86是一个强排序的平台)或内存围栏,MSVC在
    volatile
    上提供了内存围栏作为扩展,因此需要强制转换。顺便说一句,您的答案令人困惑(UmNyobe指的是)因为你说“足够”和“所需的一切”,但从未说明目标(“原子性”)。