为什么读取参考计数器值作为参考到挥发性常数?

时间:2012-12-03 19:52:39

标签: c++ boost

smart_ptr\detail\atomic_count_win32.hpp下的Boost 1.5.1源代码中,它是一个简洁的小原子参考计数器boost::detail::atomic_count

第48行,他们做了一个我很好奇的演员:

class atomic_count
{
public:

// ...

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

private:
long value_;

为什么计数器值会转换为--to-volatile-constant-long(long const volatile&)?

2 个答案:

答案 0 :(得分:5)

MSVC提供了一个现已弃用的extension on volatile variables,给予它们获取和释放语义(关于多线程编程的内存排序保证。)

此演员“在变量上启用”此扩展,为其提供读取 - 获取语义(以匹配可能还会发生的任何释放写入)。同样,这是非标准。在C ++ 11代码中,您应该使用std::atomic<>

他们需要这个,因为boost::shared_ptr可以保证多线程(共享)使用中shared_ptr<T>的正确性;这是他们对lock-free计数器的实现。

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

要在它开始之前将它扼杀在萌芽状态:没有此扩展名volatile对多线程编程没有用。甚至不尝试。这个扩展名已弃用,所以如果可以,你应该真的避免使用它。

答案 1 :(得分:1)

在x86平台上,对于原生宽度的对齐值,已知这已足够。

他们试图避免的问题是:

  1. 变量的十六进制值为0000FFFF

  2. 线程A开始读取值并获取0000xxxx部分。

  3. 主题B将值从0000FFFF增加到00010000

  4. 线程A完成读取值,获取尚未读取的xxxx0000部分。

  5. 线程A现在读取的值为00000000

  6. 这称为单词撕裂。但是,众所周知,x86上的对齐类型的原生宽度不会发生这种情况。因此,只需要通过volatile(已知可避免有问题的编译器优化)进行强制转换。

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