简体   繁体   English

停止等待std :: condition_variable的C ++ 11 std :: threads

[英]Stopping C++ 11 std::threads waiting on a std::condition_variable

I am trying to understand the basic multithreading mechanisms in the new C++ 11 standard. 我试图了解新的C ++ 11标准中的基本多线程机制。 The most basic example I can think of is the following: 我能想到的最基本的示例如下:

  • A producer and a consumer are implemented in separate threads 生产者和使用者在单独的线程中实现
  • The producer places a certain amount of items inside a queue 生产者将一定数量的项目放入队列中
  • The consumer takes items from the queue if there are any present 消费者从队列中取出物品(如果有)

This example is also used in many school books about multithreading and everything about the communication process works fine. 许多有关多线程的教科书中也使用了此示例,并且有关通信过程的所有内容都正常运行。 However, I have a problem when it comes to stopping the consumer thread. 但是,在停止使用者线程时,我遇到了问题。

I want the consumer to run until it gets an explicit stop signal (in most cases this means that I wait for the producer to finish so I can stop the consumer before the program is ended). 我希望使用者继续运行,直到获得明确的停止信号为止(在大多数情况下,这意味着我等待生产者完成,以便可以在程序结束之前停止使用者)。 Unfortunately C++ 11 threads lack an interrupt mechanism (which I know from multithreading in Java for example). 不幸的是,C ++ 11线程缺少中断机制(例如,我从Java中的多线程知道这一点)。 Thus, I have to use flags like isRunning to signal that I want a thread to stop. 因此,我必须使用isRunning标志来表示我希望线程停止。

The main problem now is: After I have stopped the producer thread, the queue is empty and the consumer is waiting on a condition_variable to get a signal when the queue is filled again. 现在的主要问题是:在停止生产者线程之后,队列为空,并且使用者在等待condition_variable等待再次填充队列时获得信号。 So I need to wake the thread up by calling notify_all() on the variable before exiting. 因此,我需要在退出之前通过在变量上调用notify_all()来唤醒线程。

I have found a working solution, but it seems somehow messy. 我找到了一个可行的解决方案,但似乎有些混乱。 The example code is listed below (I am sorry but somehow I couldn't reduce the code size any furhter for a "minimal" minimal example): 下面列出了示例代码(很抱歉,但是对于“最小”最小示例,我不知何故不能减小代码大小):

The Queue class: 队列类:

class Queue{
public:
    Queue() : m_isProgramStopped{ false } { }

    void push(int i){
        std::unique_lock<std::mutex> lock(m_mtx);
        m_q.push(i);
        m_cond.notify_one();
    }

    int pop(){
        std::unique_lock<std::mutex> lock(m_mtx);
        m_cond.wait(lock, [&](){ return !m_q.empty() || m_isProgramStopped; });

        if (m_isProgramStopped){
            throw std::exception("Program stopped!");
        }

        int x = m_q.front();
        m_q.pop();

        std::cout << "Thread " << std::this_thread::get_id() << " popped " << x << "." << std::endl;
        return x;
    }

    void stop(){
        m_isProgramStopped = true;
        m_cond.notify_all();
    }

private:
    std::queue<int> m_q;
    std::mutex m_mtx;
    std::condition_variable m_cond;
    bool m_isProgramStopped;
};

The Producer: 生产者:

class Producer{
public:
    Producer(Queue & q) : m_q{ q }, m_counter{ 1 } { }

    void produce(){
        for (int i = 0; i < 5; i++){
            m_q.push(m_counter++);
            std::this_thread::sleep_for(std::chrono::milliseconds{ 500 });
        }
    }

    void execute(){
        m_t = std::thread(&Producer::produce, this);
    }

    void join(){
        m_t.join();
    }

private:
    Queue & m_q;
    std::thread m_t;

    unsigned int m_counter;
};

The Consumer: 消费者:

class Consumer{
public:
    Consumer(Queue & q) : m_q{ q }, m_takeCounter{ 0 }, m_isRunning{ true }
    { }

    ~Consumer(){
        std::cout << "KILL CONSUMER! - TOOK: " << m_takeCounter << "." << std::endl;
    }

    void consume(){
        while (m_isRunning){
            try{
                m_q.pop();
                m_takeCounter++;
            }
            catch (std::exception e){
                std::cout << "Program was stopped while waiting." << std::endl;
            }
        }
    }

    void execute(){
        m_t = std::thread(&Consumer::consume, this);
    }

    void join(){
        m_t.join();
    }

    void stop(){
        m_isRunning = false;
    }

private:
    Queue & m_q;
    std::thread m_t;

    unsigned int m_takeCounter;
    bool m_isRunning;
};

And finally the main() : 最后是main()

int main(void){
    Queue q;

    Consumer cons{ q };
    Producer prod{ q };

    cons.execute();
    prod.execute();

    prod.join();

    cons.stop();
    q.stop();

    cons.join();

    std::cout << "END" << std::endl;

    return EXIT_SUCCESS;
}

Is this the right way to end a thread that is waiting an a condition variable or are there better methods? 这是结束等待条件变量的线程的正确方法还是有更好的方法? Currently, the queue needs to know if the program has stopped (which in my opinion destroys the loose coupling of the components) and I need to call stop() on the queue explicitly which doesn't seem right. 当前,队列需要知道程序是否已停止(我认为这会破坏组件的松散耦合),并且我需要在队列上显式调用stop() ,这似乎不正确。

Additionaly, the condition variable which should just be used as a singal if the queue is empty now stands for another condition - if the program has ended. 另外,如果队列为空,则应仅用作条件变量的条件变量现在代表另一个条件-如果程序已结束。 If I am not mistaken, every time a thread waits on a condition variable for some event to happen, it would also have to check if the thread has to be stopped before continuing its execution (which also seems wrong). 如果我没记错的话,每次线程等待条件变量发生某种事件时,它还必须检查是否必须在继续执行之前停止线程(这似乎也是错误的)。

Do I have these problems because my entire design is faulty or am I missing some mechanisms that can be used to exit threads in a clean way? 是因为我的整个设计有问题还是因为缺少一些可以干净地退出线程的机制而出现这些问题?

No, there's nothing wrong with your design, and it's the normal approach taken for this sort of problem. 不,您的设计没有错,这是解决此类问题的常规方法。

It's perfectly valid for you to have multiple conditions (eg anything on queue or program stopping) attached to a condition variable. 对于条件变量附加多个条件(例如,队列中的任何内容或程序正在停止)对您来说是完全有效的。 The key thing is that the bits in the condition are checked for when the wait returns. 关键是要在wait返回时检查条件中的位。

Instead of having a flag in Queue to indicate that the program is stopping you should think of the flag as "can I accept". 不要在Queue中有一个标志来表明程序正在停止,您应该将标志视为“我可以接受”。 This is a better overall paradigm and works better in a multi-threaded environment. 这是一个更好的整体范例,并且在多线程环境中效果更好。

Also, instead of having pop throw an exception if someone calls it and stop has been called you could replace the method with bool try_pop(int &value) which will return true if a value was returned, otherwise false . 另外,如果有人调用它并stop调用, pop不会抛出异常,而是可以用bool try_pop(int &value)替换该方法,如果返回bool try_pop(int &value) ,它将返回true ,否则返回false This way the caller can check on failure to see if the queue has been stopped (add a bool is_stopped() const method). 这样,调用方可以检查失败以查看队列是否已停止(添加bool is_stopped() const方法)。 Although exception handling works here it's a bit heavy handed and isn't really an exceptional case in a multi-threaded program. 尽管异常处理在这里起作用,但处理起来有点繁重,而且在多线程程序中并不是真正的例外情况。

wait can be called with a timeout. wait可以超时。 Control is returned to the thread and stop could be checked. 控制权返回到线程,可以检查stop Depending on that value it can wait on more items to be consumed or finish execution. 根据该值,它可以wait消耗更多项目或完成执行。 A good introduction to multithreading with c++ is C++11 Concurrency . C ++ 11 ConcurrencyC ++多线程的一个很好的介绍。

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

相关问题 c++ 中的 std::condition_variable::wait - std::condition_variable::wait in c++ C++。 std::condition_variable 和多个等待线程 - C++. std::condition_variable and multiple wait-threads C++ std::condition_variable 在 class 上下文中 - C++ std::condition_variable in class context 在 C++ 中,std::condition_variable 的 boolean 谓词是否应该是 volatile 的? - Should a boolean predicate for std::condition_variable be volatile in C++? 使用std :: atomic比较std :: condition_variable wrt在C ++中暂停和恢复std :: thread的方法 - Approach of using an std::atomic compared to std::condition_variable wrt pausing & resuming an std::thread in C++ C ++ 11:为什么std :: condition_variable使用std :: unique_lock? - C++11: why does std::condition_variable use std::unique_lock? C ++ 11 - 无法使用std :: thread和std :: condition_variable唤醒线程 - C++11 - can't awake a thread using std::thread and std::condition_variable 必须在等待之前检查 std::condition_variable 谓词吗? - Must the std::condition_variable predicate be checked before waiting? 为什么 std::condition_variable 不等待并立即再次获得锁定? - Why is std::condition_variable not waiting and immediately getting the lock again? 在 C++ 中使用 std::thread 和 std::condition_variable 自定义后台任务的 SIGABRT - SIGABRT on custom background task using std::thread and std::condition_variable in C++
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM