这是问题如何 std::atomic<T>::notify_all 排序?
如果我使用WaitOnAddress
或futex
直接使用,该问题的答案是什么。
从该问题的答案得出的结论是,以下程序不会挂起,并且 C++ 提供了必要的保证,尽管措辞仍然可能引发问题:
#include <atomic>
#include <chrono>
#include <thread>
int main()
{
std::atomic<bool> go{ false };
std::thread thd([&go] {
go.wait(false, std::memory_order_relaxed); // (1)
});
std::this_thread::sleep_for(std::chrono::milliseconds(400));
go.store(true, std::memory_order_relaxed); // (2)
go.notify_all(); // (3)
thd.join();
return 0;
}
现在让我们考虑将这个程序翻译成纯 Windows API,没有 C++20 甚至 C++11:
#include <Windows.h>
#pragma comment(lib, "Synchronization.lib")
volatile DWORD go = 0;
DWORD CALLBACK ThreadProc(LPVOID)
{
DWORD zero = 0;
while (go == zero)
{
WaitOnAddress(&go, &zero, sizeof(DWORD), INFINITE); // (1)
}
return 0;
}
int main()
{
HANDLE thread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
if (thread == 0)
{
return 1;
}
Sleep(400);
go = 1; // (2)
WakeByAddressAll(&go); // (3)
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
return 0;
}
由于虚假唤醒,我添加了循环。
所以同样的问题在这里。如果在(1)中以相反的顺序观察(2)和(3),则可能由于丢失通知而挂起。WinAPI 是否会阻止这种情况,或者需要明确设置围栏。
这个问题的答案的实际应用是std::atomic<T>::wait
在 Windows 平台上由标准库或替代标准库实现。
futex
在 Linux 平台实现的上下文中,我也有同样的问题。