当前位置: 首页 > 知识库问答 >
问题:

std::condition\u变量::notify\u one:如果某些线程具有假谓词,它是否会唤醒多个线程?

柴辰阳
2023-03-14

我有一个用于读写器的环形缓冲区。我跟踪环形缓冲区中的数字if条目,不允许覆盖尚未读取的条目。我使用std::condition\u variable wait()和notify\u one()来同步读写器。读卡器的基本条件是

这一切似乎都奏效了,但有一件事我不明白。当读写器调用notify\u one()时,不会导致上下文切换。我读过并理解它是这样工作的。然而,在写入程序写入一个条目以填充缓冲区的情况下,写入程序html" target="_blank">调用notify\u one()并继续写入另一个条目,在这种情况下,其谓词在其wait()中失败。在本例中,我看到另一个writer()可能会被唤醒,其谓词也会失败。然后,读取器将醒来,其谓词成功,并可以开始阅读。

我不明白的是,为什么一个notify_one()上有多个线程被解锁。谓词失败的wait()是否不会耗尽notify?我找不到任何说明这是事实的东西。

我可以调用notify\u all()来确定,但它似乎正在使用notify\u 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;
}

我在输出中看到以下内容,其中唤醒了多个线程,因为谓词为false。

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

共有1个答案

唐兴思
2023-03-14

当您的每个读取线程检查是否应该等待,或者条件是否已经满足时,您将看到条件的初始测试。

从这里开始,wait()的重载等价于

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

因此,仅当条件为true时才调用etc(),但必须首先检查条件。

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个值,并且每个读卡器只读取一个值,这可能会很明显。

 类似资料:
  • 我有2个工作线程和1个处理线程。 当处理线程正在尝试处理某些事情,而辅助线程正在执行它们的工作时,处理线程应该等待,并且在辅助线程中执行的所有作业完成时唤醒。 我怎样才能唤醒这根线?我将尝试演示我在这段伪代码中的意思 处理线程类似于 这样的事情可能发生吗?让线程等待到多个调用notifyAll()的源,而不是只等待一次。我希望我把这个问题弄清楚了。 多谢帮忙!

  • 本文向大家介绍Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll),包括了Java多线程基础 线程的等待与唤醒(wait、notify、notifyAll)的使用技巧和注意事项,需要的朋友参考一下 本篇我们来研究一下 wait() notify() notifyAll() 。 DEMO1: wait() 与 notify() DEMO1 输出: 注意: 使用 wait

  • 根据cppreference,std::atomic::notify\u one()将通知至少一个等待所述atomic的线程。这意味着根据标准,它可以解锁多个线程。这与std::condition\u variable::notify\u one()相反,后者指定它将解锁(不超过)一个线程。 这种差异来自哪里?这不是使用相同的底层机制吗?就标准库的实现而言,是否所有流行的都有机会通过此调用实际解除

  • 问题内容: 看到各种与锁定相关的问题,并且(几乎)总是发现“由于虚假唤醒而引起的循环” 1我想知道,有人经历过这种唤醒(例如,假设硬件/软件环境不错)吗? 我知道“虚假”一词没有明显的原因,但是发生此类事件的原因可能是什么? (1注意:我不是在问循环练习。) 编辑:一个帮助器问题(对于那些喜欢代码示例的人): 如果我有以下程序,并且运行它: 我该怎么做才能虚假地唤醒它,而不必永远等待随机事件? 问

  • 问题内容: 哈罗我已经整天调试了我的代码,但是我看不出哪里出了问题。 我在主线程上使用SerialPortEventListener,在工作线程中,我有一个客户端套接字与服务器通信。由于到达此工作线程之后,我仍然需要在主线程中完成一些总结工作,因此我想创建一个“伪线程”,在主线程中等待,直到从侦听器onEvent方法通知它为止。 但是这个伪线程似乎一直在等待。 我检查了锁定的线程,它们在Runna