如何避免将 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.
我的程序由一堆快乐地运行的线程组成,我唯一的同步是一个 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.