使用易失性布尔变量忙于等待

Using volatile boolean variable for busy waiting

本文关键字:等待 于等待 易失性 布尔 变量      更新时间:2023-10-16

在阅读了其他开发人员编写的一些代码后出现了这个问题,所以我做了一些研究,我找到了Andrei Alexandrescu的文章。在他的文章中,他说可以使用易失性布尔变量来忙于等待(参见Wait/Wakeup的第一个示例)

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

我真的不明白它是如何工作的。

  1. 易失性不保证操作是原子的。实际上对布尔变量的读取/写入是原子的,但理论并不能保证这一点。从我的角度来看,上面的代码可以通过使用 std::atomic::load/store 函数以及相应的获取/释放内存排序约束,使用 C++11 安全地重写。
  2. 在所描述的示例中,我们没有这个问题,但是如果我们有多个写入,则可能会遇到内存排序问题。易失性不是围栏,它不会强制内存排序,它只是阻止编译器优化。

那么,为什么这么多人使用挥发性布尔值来忙碌等待,它真的是便携式的吗?

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

如果您

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

我真的不明白它是如何工作的。

它依赖于两个假设:

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

第一种可能会保留任何理智的架构。第二种适用于任何单核架构,以及目前广泛使用的多核架构,但不能保证它将来会继续存在。

上面的代码可以通过使用 C++11 安全地重写std::atomic

今天,它可以而且应该如此。在2001年,当这篇文章写出来时,并没有那么多。

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

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

为什么这么多人使用挥发性布尔值来忙碌等待

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

它真的是便携式的吗?

不。C++11 内存模型并不能保证任何这些假设,随着典型内核数量的增加,它们很有可能对于未来的硬件支持变得不切实际。 volatile从来都不是线程同步的解决方案,现在语言确实提供了正确的解决方案,这加倍了。