一個簡單的使用條件變量的場景:主線程從stdin讀取一個字符串,然後由echo線程打印這個字符串,正確的代碼是這樣的:
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <string>
#include <stdio.h>
static std::condition_variable cv;
static std::mutex m;
static bool ready = false;
static std::string s;
static void echoWorker()
{
std::unique_lock<std::mutex> lock(m);
while (!ready)
{
cv.wait(lock);
}
std::cout << "echo string: " << s << std::endl;
}
int main()
{
const int BUF_LEN = 20;
std::thread t([](){ echoWorker(); });
char buffer[BUF_LEN];
fgets(buffer, BUF_LEN, stdin);
{
std::unique_lock<std::mutex> lock(m);
s = buffer;
ready = true;
std::cout << "string ready" << std::endl;
}
cv.notify_one();
t.join();
return 0;
}
主線程爲什麼要使用鎖來保護s
和ready
?網上很多文章和帖子都說是爲了防止cv.notify_one()
的“喚醒丟失”,我覺得這表述有些含糊不清。請看下面這種執行時序:
主線程的任務都執行結束後,纔開始執行echo線程的任務。在這種情況下,主線程的cv.notify_one()
調用沒有喚醒任何線程,可以說是“喚醒丟失”了,但是echo線程依然能夠完成應做的工作。所以說“喚醒丟失”並不是出bug的充要條件。notify_one
只要比wait
執行得早,就會“喚醒丟失”,這“喚醒丟失”如果發生在!ready
判斷前的話就並不是問題。
那麼互斥量究竟起到了什麼作用呢?我認爲互斥量起到的作用是將!ready
判斷和cv.wait()
操作這兩項工作原子化了,假如echo線程在運行!ready
和cv.wait()
中間被主線程插了一腳,把ready
置true
,同時執行了cv.notify_one()
,那echo線程就會永遠等下去了,這就出bug了。所以我們要把ready
變量保護起來,讓主線程插不進這一腳,我覺得這纔是互斥量的真正作用。