![](/img/trans.png)
[英]How std::condition_variable and std::mutex works exactly?
[英]Mutex protecting std::condition_variable
即使共享變量是原子的,也必須在互斥鎖下進行修改,才能將修改正確發布到等待線程。 任何打算等待 std::condition_variable 的線程都必須在用於保護共享變量的同一互斥鎖上獲取 std::unique_lock
我明白,通過使用互斥鎖保護 std::condition_variable,我們可以防止在等待線程實際上沒有等待時丟失通知。 這里已經回答: 如果沒有在互斥鎖下修改共享原子變量,則不會正確發布
我想知道的是,是否可以僅使用互斥鎖來保護 std::condition_variable 以及對共享數據的某種其他形式的保護? 如果我們修改另一個答案中給出的示例,這會起作用嗎?
std::atomic_bool proceed(false);
std::mutex m;
std::condition_variable cv;
std::thread t([&m,&cv,&proceed]()
{
{
std::unique_lock<std::mutex> l(m);
while(!proceed) {
hardWork();
cv.wait(l);
}
}
});
proceed = true;
{
std::lock_guard<std::mutex> lock(m);
}
cv.notify_one();
t.join();
或者我錯過了內存排序或緩存有什么問題嗎?
更新
我知道互斥鎖通常也保護共享數據,使用原子變量只是一個例子。 問題不在於如何保護共享數據,而在於是否有必要使用相同的互斥鎖來保護兩者。 另一個使用第二個互斥鎖的例子:
bool proceed(false);
std::mutex boolMutex;
std::mutex cvMutex;
std::condition_variable cv;
std::unique_lock<std::mutex> l(cvMutex);
void setBool()
{
std::lock_guard<std::mutex> lock(boolMutex);
proceed = true;
}
bool checkBool()
{
std::lock_guard<std::mutex> lock(boolMutex);
return proceed;
}
void worker()
{
while (true)
{
cv.wait(l);
if (checkBool()) {
// Do work
return;
}
}
}
int main()
{
std::thread t(worker);
setBool();
{
std::lock_guard<std::mutex> lock(cvMutex);
}
cv.notify_one();
t.join();
return 0;
}
必須設置 mutex-protected 標志,並通知條件變量,而 mutex 仍被保留:
{
std::lock_guard<std::mutex> lock(m);
proceed = true;
cv.notify_one();
}
此外,在這種情況下, proceed
標志不需要是原子實體。 一個簡單的
bool proceed;
就足夠了。 只有在持有相關聯的互斥鎖時才能訪問proceed
,因此使proceed
原子化絕對不會完成任何事情。
atomic
實體用於處理不涉及任何互斥鎖的奇異並發情況。
我不認為山姆的回答是正確的。 考慮以下代碼:
// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);
// thread #2:
proceed = true; // atomic to avoid data race
cv.notify_one();
這里的問題是以下可能的事件序列:
thread #1: while (!proceed) // evaluated as true
thread #2: proceed = true;
thread #2: cv.notify_one();
thread #1: cv.wait(l); // never gets notified
為了避免這種情況,一個典型的解決方案是使用相同的互斥鎖保護proceed
修改:
// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);
// thread #2:
{
std::lock_guard<std::mutex> l(m);
proceed = true; // does not need to be atomic
}
cv.notify_one();
現在, proceed = true;
必須發生在while (!proceed)
或之后cv.wait(l);
開始等待; 都可以。 在第一種情況下,根本不需要等待; 在第二種情況下, cv.notify_one();
保證僅在cv.wait(l);
時發生cv.wait(l);
實際上是在等待。
現在,你的(學術類)案例呢?
// thread #1:
std::unique_lock<std::mutex> l(m);
while (!proceed) cv.wait(l);
// thread #2:
proceed = true; // atomic to avoid data race
{
std::lock_guard<std::mutex> lock(m);
}
cv.notify_one();
我相信這種情況也是完全有效的,因為上述錯誤情況也不會發生。 原因很簡單。 如果while (!proceed)
被評估為 false,同樣,沒有等待。 並且,如果while (!proceed)
被評估為真,則通知不會發生,直到cw.wait(l);
被調用。
結論
我相信你的第一個例子沒問題,cppreference 的引用是不正確的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.