如何避免将 volatile 用于无锁程序?

How to avoid using volatile for lockfree program?

我的程序由一堆快乐地运行的线程组成,我唯一的同步是一个 volatile 全局 bool,它告诉它们用户是否退出。线程之间的所有其他通信都是无锁的。在时间非常关键的应用程序中,这些线程不断有工作要做,所以我不能承受它们之间的锁。我最近看到很多信息表明 volatile 不利于多线程,所以我想使我的代码更好。我看到 std::atomic_flag 保证无锁,但我不知道如何在我的案例中使用它。

基本设置如下(省略代码所在的不同文件):

// Global variable in its own .h file
extern volatile bool SystemOnline;

// Initialization in a .cpp file    
volatile bool SystemOnline = true;

// Windows message processing
while (SystemOnline)
{
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        if (msg.message == WM_QUIT)
        {
            SystemOnline = false;
        }
        else if (!TranslateAccelerator(msg.hwnd, NULL, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}

// Thread functions
void Thread1()
{
    while (SystemOnline)
    {
        // Do some work
    }
}

void Thread2()
{
    while (SystemOnline)
    {
        // Do some other work
    }
}

// And so on...

最后加入所有线程。

首先,让我们先解决更大的性能问题。您的主 Win32 线程正在旋转而无需等待。这将抵消无锁 bool 和 std::atomic.

之间的任何感知性能差异

只要在空队列上冗余调用 PeekMessage,您就会烧掉整个核心。所以不是这个:

while (SystemOnline)
{
    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
        if (msg.message == WM_QUIT)
        {
            SystemOnline = false;
        }
        else if (!TranslateAccelerator(msg.hwnd, NULL, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}

让我们执行以下操作完全等同于,除了 GetMessage 将阻塞直到传入消息到达。当 WM_QUIT 消息出列时,GetMessage returns FALSE。

MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
    if (!TranslateAccelerator(msg.hwnd, NULL, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}
SystemOnline = false;

正如其他人所指出的,从全局 bool 到 atomic 的转换非常简单:

只需更改此全局:

// Global variable in its own .h file
extern volatile bool SystemOnline;

// Initialization in a .cpp file    
volatile bool SystemOnline = true;

为此:

// Global variable in its own .h file
#include <atomic>
extern std::atomic<bool> SystemOnline;

// Initialization in a .cpp file    
std::atomic<bool> SystemOnline(true);

您也可以使用 std::atomic_bool - 它是 std::atomic<bool>

的类型定义

这就是您要做的全部。您甚至不必更改线程代码,因为此类型的 () 运算符只是调用对象上的 load() 方法。同样,主线程循环结束时的 = false 赋值与调用 SystemOnline.store(false).

相同

正如其他人指出的那样,使用 atomic 并没有太大的性能损失。在 x86 上,它映射到 LOCK opcode prefix.