簡體   English   中英

std::condition_variable::notify_one: 如果有些線程有錯誤的謂詞,它會喚醒多個線程嗎?

[英]std::condition_variable::notify_one: does it wake multiple threads if some have false predicate?

我有一個用於讀/寫的環形緩沖區。 如果環形緩沖區中的條目,我會跟蹤數字,並且不允許覆蓋尚未讀取的條目。 我使用 std::condition_variable wait() 和 notify_one() 來同步讀取器和寫入器。 基本上閱讀器的條件是條目數> 0。寫入器的條件是條目數<容量。

這一切似乎都有效,但有一件事我不明白。 當讀取器或寫入器調用 notify_one() 時,不會導致上下文切換。 我已經閱讀並理解它是這樣工作的。 但是,在寫入者寫入條目以填充緩沖區的情況下,寫入者調用 notify_one() 並繼續寫入另一個,在這種情況下,其謂詞在其 wait() 中失敗。 在這種情況下,我看到另一個 writer() 可能會醒來,並且它的謂詞也會失敗。 然后閱讀器將醒來並且其謂詞成功並且可以開始閱讀。

我不明白為什么在一個 notify_one() 多個線程被解除阻塞。 帶有失敗謂詞的 wait() 不會吃掉通知嗎? 我找不到任何說明這種情況的東西。

我可以調用 notify_all() 來確定,但它似乎正在使用 notify_one()。

這是代碼。

#include <iostream>
#include <stdint.h>

#include <boost/circular_buffer.hpp>
#include <condition_variable>
#include <thread>


// ring buffer with protection for overwrites 
template <typename T>
class ring_buffer {

  public:

    ring_buffer(size_t size) {
        cb.set_capacity(size);
    }

    void read(T& entry) {
        {
            std::unique_lock<std::mutex> lk(cv_mutex);
            cv.wait(lk, [this] {
                    std::cout << "read woke up, test=" << (cb.size() > 0) << std::endl; 
                    return 0 < cb.size();});
            auto iter = cb.begin();
            entry = *iter;
            cb.pop_front(); 
            std::cout << "Read notify_one" << std::endl;
        }
        cv.notify_one();
    } 

    void write(const T& entry) {
        {
            std::unique_lock<std::mutex> lk(cv_mutex);
            //std::cout << "Write wait" << std::endl;
            cv.wait(lk, [this] {
                    std::cout << "write woke up, test=" << (cb.size() < cb.capacity()) << std::endl; 
                    return cb.size() < cb.capacity();});
            cb.push_back(entry);
            std::cout << "Write notify_one" << std::endl;
        }
        cv.notify_one();
    }

    size_t get_number_entries() {
        std::unique_lock<std::mutex> lk(cv_mutex);
        return cb.size();
    }

  private:

    boost::circular_buffer<T> cb;
    std::condition_variable cv;
    std::mutex cv_mutex;
};

void write_loop(ring_buffer<int> *buffer) {

    for (int i = 0; i < 100000; ++i) {
        buffer->write(i);
    }
}

void read_loop(ring_buffer<int> *buffer) {

    for (int i = 0; i < 50000; ++i) {
        int val;
        buffer->read(val);
    }

}

int main() {

    ring_buffer<int> buffer(1000); 
    std::thread writer(write_loop, &buffer);
    std::thread reader(read_loop, &buffer);
    std::thread reader2(read_loop, &buffer);

    writer.join();
    reader.join();
    reader2.join();

    return 0;
}

我在 output 中看到以下內容,其中多個線程被喚醒,因為謂詞為假。

read woke up, test=0 
read woke up, test=0 
write woke up, test=1 

當您的每個讀取線程檢查它是否應該等待或條件是否已經滿足時,您會看到條件的初始測試。

這里開始,這個 wait() 的重載相當於

while (!pred()) {
    wait(lock);
}

所以 wait() 僅在條件為true時調用,但必須先檢查條件。

read woke up, test=0  // tests condition on reader1 thread, false, wait is called
read woke up, test=0  // tests condition on reader2 thread, false, wait is called
write woke up, test=1 // tests condition on writer thread, true, wait is not called

可能會使寫入 2 個值的位置變得很明顯,並且每個讀取器只會讀取一個值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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