簡體   English   中英

互斥保護 std::condition_variable

[英]Mutex protecting std::condition_variable

即使共享變量是原子的,也必須在互斥鎖下進行修改,才能將修改正確發布到等待線程。 任何打算等待 std::condition_variable 的線程都必須在用於保護共享變量的同一互斥鎖上獲取 std::unique_lock

http://en.cppreference.com/w/cpp/thread/condition_variable

我明白,通過使用互斥鎖保護 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM