使用volatile布尔变量进行忙等待

时间:2013-11-12 15:39:22

标签: c++ multithreading volatile memory-fences busy-waiting

在阅读其他开发人员编写的一些代码之后出现了问题,所以我做了一些研究,我找到了Andrei Alexandrescu撰写的文章。在他的article中,他说可以使用volatile布尔变量进行忙等待(参见第一个带等待/唤醒的例子)

class Gadget
{
public:
    void Wait()
    {
        while (!flag_)
        {
            Sleep(1000); // sleeps for 1000 milliseconds
        }
    }
    void Wakeup()
    {
        flag_ = true;
    }
    ...
private:
    bool flag_;
};

我真的不知道它是如何运作的。

  1. volatile不保证操作是原子的。实际上对布尔变量的读/写是原子的,但理论并不能保证这一点。从我的角度来看,上面的代码可以通过使用std :: atomic :: load / store函数和相应的获取/释放内存排序约束来安全地用C ++ 11重写。
  2. 我们在上述示例中没有遇到此问题,但如果我们有多次写入,我们可能会遇到内存排序问题。易失性不是围栏,它不会强制内存排序,它只是阻止编译器优化。
  3. 那么为什么这么多人使用volatile bool进行忙碌等待并且它是否真的便携?

2 个答案:

答案 0 :(得分:2)

文章没有说volatile就是你所需要的(事实上,它不是),只是说它有用。

  

如果你这样做,,如果你使用简单的通用组件LockingPtr ,你可以编写线程安全的代码,而不用担心竞争条件,因为编译器会担心你并且会努力指出你错的地方。

答案 1 :(得分:0)

  

我真的不知道它是如何运作的。

它依赖于两个假设:

  • 读取和写入布尔变量是原子的;
  • 所有线程都有统一的内存视图,因此在一个线程上进行的修改在很短的时间内对其他线程可见,而没有明确的内存障碍。

第一个很可能坚持任何理智的架构。第二种方法适用于任何单核架构,以及当今广泛使用的多核架构,但不能保证它将来会继续存在。

  

使用std::atomic

可以使用C ++ 11安全地重写上面的代码

今天,它可以而且应该如此。在2001年撰写文章时,并非如此。

  

如果我们有多个写入,我们可能会遇到内存排序问题

事实上。如果此机制用于与其他数据同步,那么我们依赖于第三个假设:保留修改顺序。同样,大多数流行的处理器都会提供这种行为,但不能保证这种情况会持续下去。

  

为什么这么多人使用volatile bool进行忙碌等待

因为在C ++获得多线程内存模型之前,他们不能或不会改变他们形成的习惯。

  

是否真的便携?

没有。 C ++ 11内存模型并不能保证任何这些假设,并且随着典型内核数量的增长,它们很可能会成为未来硬件支持的不切实际的选择。 volatile从来就不是线程同步的解决方案,而且现在该语言确实提供了正确的解决方案。