我試圖演示std::condition_variable使用用 MSVC(版本 19.32.31332,如果重要的話)編譯的簡單程式的虛假喚醒行為。但是,我已經開始注意到虛假喚醒僅在wait使用該方法的兩個引數多載時發生。這在除錯和發布模式中似乎都是正確的。
Predicate我可以使用兩個引數多載的引數輕松檢測虛假喚醒:
代碼
#include <iostream>
#include <condition_variable>
#include <future>
#include <mutex>
#include <random>
#include <thread>
#include <vector>
using namespace std::chrono_literals;
#define NUM_CVS 6
#define NUM_THREADS 12
std::mutex cout_mutex;
std::mutex m[NUM_CVS];
std::condition_variable cv[NUM_CVS];
bool data[NUM_CVS];
bool check_data(std::size_t i) {
bool d = data[i];
{
std::lock_guard<std::mutex> lg(cout_mutex);
std::cout << std::this_thread::get_id() << ": Woken... " << (d ? "Waited." : "Spurious.") << std::endl;
}
return d;
}
void task(std::size_t i) {
{
std::lock_guard<std::mutex> lg(cout_mutex);
std::cout << std::this_thread::get_id() << ": Locking..." << std::endl;
}
std::unique_lock<std::mutex> l(m[i]);
{
std::lock_guard<std::mutex> lg(cout_mutex);
std::cout << std::this_thread::get_id() << ": Locked. Waiting..." << std::endl;
}
cv[i].wait(l, [&] {return check_data(i); });
}
int main()
{
std::random_device rd;
std::default_random_engine dre(rd());
std::uniform_int_distribution<std::size_t> d(0, NUM_CVS - 1);
std::vector<std::future<void>> v;
for (auto i = 0; i < NUM_THREADS; i) {
v.push_back(std::async(task, d(dre)));
}
std::this_thread::sleep_for(1000ms);
for (auto i = 0; i < NUM_CVS; i) {
auto index = i;
{
std::lock_guard<std::mutex> g(m[index]);
data[index] = true;
}
cv[index].notify_one();
}
for (auto& f : v) {
f.wait();
}
}
控制臺輸出
25220: Locking...
22672: Locking...
22672: Locked. Waiting...
22672: Woken... Spurious.
26224: Locking...
26224: Locked. Waiting...
26224: Woken... Spurious.
22356: Locking...
22356: Locked. Waiting...
22356: Woken... Spurious.
25220: Locked. Waiting...
25220: Woken... Spurious.
18304: Locking...
18304: Locked. Waiting...
18304: Woken... Spurious.
26544: Locking...
21132: Locking...
21132: Locked. Waiting...
21132: Woken... Spurious.
19144: Locking...
19144: Locked. Waiting...
19144: Woken... Spurious.
21584: Locking...
21584: Locked. Waiting...
21584: Woken... Spurious.
26544: Locked. Waiting...
26544: Woken... Spurious.
25872: Locking...
25872: Locked. Waiting...
25872: Woken... Spurious.
24928: Locking...
3956: Locking...
3956: Locked. Waiting...
3956: Woken... Spurious.
24928: Locked. Waiting...
24928: Woken... Spurious.
22356: Woken... Waited.
22672: Woken... Waited.
21584: Woken... Waited.
25220: Woken... Waited.
21132: Woken... Waited.
26224: Woken... Waited.
但是,當我不提供謂詞并在事后簡單地檢查虛假喚醒時,程式的行為就好像不再可能出現虛假喚醒:
代碼
void task(std::size_t i) {
{
std::lock_guard<std::mutex> lg(cout_mutex);
std::cout << std::this_thread::get_id() << ": Locking..." << std::endl;
}
std::unique_lock<std::mutex> l(m[i]);
{
std::lock_guard<std::mutex> lg(cout_mutex);
std::cout << std::this_thread::get_id() << ": Locked. Waiting..." << std::endl;
}
cv[i].wait(l);
check_data(i);
}
輸出
23592: Locking...
23592: Locked. Waiting...
20388: Locking...
20388: Locked. Waiting...
26536: Locking...
26536: Locked. Waiting...
18436: Locking...
18436: Locked. Waiting...
1528: Locking...
1528: Locked. Waiting...
21236: Locking...
21236: Locked. Waiting...
11784: Locking...
11784: Locked. Waiting...
21776: Locking...
21776: Locked. Waiting...
20796: Locking...
20796: Locked. Waiting...
21472: Locking...
21472: Locked. Waiting...
23988: Locking...
23988: Locked. Waiting...
24988: Locking...
24988: Locked. Waiting...
20388: Woken... Waited.
23592: Woken... Waited.
21236: Woken... Waited.
26536: Woken... Waited.
21472: Woken... Waited.
11784: Woken... Waited.
這是 MSVC 的一個不尋常的實作細節、預期的行為std::condition_variable,還是僅僅是一個錯誤?
PS我知道主執行緒可能會在任務執行緒等待它們之前發出通知。對于這個例子,我認為 1 秒的延遲足以緩解。
uj5u.com熱心網友回復:
您在第一個示例中看到的“虛假喚醒”實際上都不是虛假喚醒。如果您查看等待示例實作,則清楚地表明謂詞是在實際阻塞 CV 之前執行的。日志中恰好有 12 個虛假喚醒 - 用于您的 12 個執行緒。每個執行緒在實際阻塞之前首先檢查謂詞。
發生虛假喚醒時的檢測邏輯有誤報。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/513354.html
標籤:C 多线程c 11锁定
下一篇:佇列大小的差異給出了任意大的值C
