條件變量爲什麼要和互斥量一起使用?

一個簡單的使用條件變量的場景:主線程從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;
}

主線程爲什麼要使用鎖來保護sready?網上很多文章和帖子都說是爲了防止cv.notify_one()的“喚醒丟失”,我覺得這表述有些含糊不清。請看下面這種執行時序:

在這裏插入圖片描述

主線程的任務都執行結束後,纔開始執行echo線程的任務。在這種情況下,主線程的cv.notify_one()調用沒有喚醒任何線程,可以說是“喚醒丟失”了,但是echo線程依然能夠完成應做的工作。所以說“喚醒丟失”並不是出bug的充要條件。notify_one只要比wait執行得早,就會“喚醒丟失”,這“喚醒丟失”如果發生在!ready判斷前的話就並不是問題。

那麼互斥量究竟起到了什麼作用呢?我認爲互斥量起到的作用是將!ready判斷和cv.wait()操作這兩項工作原子化了,假如echo線程在運行!readycv.wait()中間被主線程插了一腳,把readytrue,同時執行了cv.notify_one(),那echo線程就會永遠等下去了,這就出bug了。所以我們要把ready變量保護起來,讓主線程插不進這一腳,我覺得這纔是互斥量的真正作用。

發佈了98 篇原創文章 · 獲贊 75 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章