简体   繁体   English

如何重置std :: condition_variable

[英]how to reset std::condition_variable

I am trying to use condition variable to trigger a second thread to do some work after a certain number of buffers are in a deque. 我正在尝试使用条件变量来触发第二个线程在一定数量的缓冲区处于双端队列之后进行一些工作。

  • main() is reading ADC data in a tight and time sensitive loop main()正在紧密和时间敏感的循环中读取ADC数据
  • main() adds the buffer to a deque (that is processed by a thread pool manager) main()将缓冲区添加到双端队列(由线程池管理器处理)
  • the deque depth is variable so a test in main() for depth >= 4 and then calling notify_one is not possible (ie: during heavy processing times, the depth of the deque could be as much as 200 or 300 buffers or as little as 4 during light processing) 双端队列的深度是可变的,因此无法在main()中测试深度> = 4,然后再调用notify_one(即:在繁重的处理时间中,双端队列的深度可能多达200或300个缓冲区,或者少至光处理过程中的4个)
  • thrd() never needs to signal main() thrd()永远不需要发信号main()

My problem is that in thrd(), after getting the signal and buffers < 4, it cycles back through the while loop and immediately gets the condition again. 我的问题是在thrd()中,在获得信号和缓冲区<4之后,它会循环返回while循环并立即再次获得条件。 Is there a way to reset the cv? 有没有办法重置简历?

deque<int> vbufs;
std::mutex thrdLock;
void thrd()
{
    while (true) {
        cout << "thrd() waiting for lock\n";
        std::unique_lock < std::mutex > lock(thrdLock);
        cout << "thrd() waiting for condition\n";
        cv.wait(lock, []{ return (vbufs.size() > 0); });
        thrdLock.unlock();
        cout << "thrd() condition set\n";

        if (vbufs.size() >= 4) {    // pretend to do something with 4 buffers and remove them
            std::lock_guard < std::mutex > lock(thrdLock);
            vbufs.pop_front();
            vbufs.pop_front();
            vbufs.pop_front();
            vbufs.pop_front();
            cout << "thrd() reducing buffers:" << vbufs.size() << endl;
        }
    }
}

int main(int argc, char* argv[]) {
    std::thread t1(thrd);
    int tctr = 0;

    while (true) {
        usleep(1000);

        {
            cout << "main() waiting for lock\n";
            std::lock_guard < std::mutex > lock(thrdLock);
            vbufs.push_back(++tctr);
            cout << "main() incremented buffers:" << vbufs.size() << endl;
        }
        cv.notify_one();
    }
    return 0;
}

You can't reset a condition variable; 您不能重置条件变量; it makes no sense - notifying it doesn't change its state so there is nothing to reset. 这没有任何意义-通知它不会更改其状态,因此无需重置。 When it is notified only a thread already waiting can be awoken. 收到通知后,只能唤醒已经等待的线程。 If that thread then waits again it will not continue until the condition variable is re-notified. 如果该线程然后再次等待,它将不会继续,直到重新通知条件变量。

If you only want to do work when there are four or more buffers, shouldn't you change your wait like this? 如果您只想在有四个或更多缓冲区的情况下进行工作,是否不应该这样更改等待时间?

cv.wait(lock, []{ return (vbufs.size() >= 4); });

What's much more important is that you are potentially reading and writing vbufs simultaneously from two different threads - your if (vbufs.size() >= 4) occurs outside the lock, and so can happen at the same time as the calls to push_back . 更重要的是,您可能vbufs同时从两个不同的线程读取和写入vbufs您的if (vbufs.size() >= 4)发生在锁之外,因此可能与对push_back的调用同时发生。 That leads to undefined behaviour, which may explain what you are seeing. 这导致不确定的行为,可能会解释您所看到的。

I found a couple of bugs in the program - corrected here: 我在程序中发现了两个错误-此处已纠正:

Note: the way you were unlocking the mutex directly rather than through the std::unique_lock was causing a data race. 注意:您直接解锁互斥锁而不是通过std::unique_lock解锁的方式导致了数据争用。

#include <iostream>
#include <deque>
#include <mutex>
#include <thread>
#include <chrono>
#include <condition_variable>

std::deque<int> vbufs;
std::mutex thrdLock;
std::condition_variable cv;

template<class...Ts>
void emit(Ts&&...ts)
{
    static std::mutex m;
    std::lock_guard<std::mutex> lg(m);
    using expand = int[];
    void(expand { 0, ((std::cout << std::forward<Ts>(ts)), 0)... });
}

void thrd()
{
    while (true) {
        emit("thrd() waiting for lock\n");
        std::unique_lock < std::mutex > lock(thrdLock);
        emit("thrd() waiting for condition\n");
        cv.wait(lock, []{ return vbufs.size() >= 4; });
        emit("thrd() condition set\n");
        auto a = vbufs.front(); vbufs.pop_front();
        auto b = vbufs.front(); vbufs.pop_front();
        auto c = vbufs.front(); vbufs.pop_front();
        auto d = vbufs.front(); vbufs.pop_front();
        emit("thrd() reducing buffers:", vbufs.size(), '\n');
        lock.unlock();
        emit(a, ' ', b, ' ', c, ' ', d, '\n');
    }
}

int main(int argc, char* argv[]) {
    std::thread t1(thrd);
    int tctr = 0;

    while (true) {
        std::this_thread::sleep_for(std::chrono::microseconds(10));

        {
            emit("main() waiting for lock\n");
            std::lock_guard < std::mutex > lock(thrdLock);
            vbufs.push_back(++tctr);
            emit("main() incremented buffers:", vbufs.size(), '\n');
        }
        cv.notify_one();
    }
    t1.join(); // un-necessary in this case, but...
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM