停止等待std :: condition_variable的C ++ 11 std :: threads [英] Stopping C++ 11 std::threads waiting on a std::condition_variable

查看:105
本文介绍了停止等待std :: condition_variable的C ++ 11 std :: threads的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解新的C ++ 11标准中的基本多线程机制.我能想到的最基本的示例如下:

I am trying to understand the basic multithreading mechanisms in the new C++ 11 standard. The most basic example I can think of is the following:

  • 生产者和消费者在单独的线程中实现
  • 生产者将一定数量的物品放入队列中
  • 如果有礼物,消费者从队列中取出物品

在许多有关多线程的教科书中也使用了此示例,并且有关通信过程的所有内容都正常运行.但是,当涉及到停止使用者线程时,我遇到了一个问题.

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.

我希望使用者继续运行,直到获得明确的停止信号为止(在大多数情况下,这意味着我等待生产者完成,以便可以在程序结束之前停止使用者).不幸的是,C ++ 11线程缺少中断机制(例如,我从Java中的多线程知道这一点).因此,我必须使用isRunning之类的标志来表示我希望线程停止.

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). Thus, I have to use flags like isRunning to signal that I want a thread to stop.

现在的主要问题是:在我停止了生产者线程之后,队列为空,并且使用者在等待condition_variable来等待再次填充队列时获得信号.因此,我需要在退出之前通过在变量上调用notify_all()来唤醒线程.

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. So I need to wake the thread up by calling notify_all() on the variable before exiting.

我找到了一个可行的解决方案,但似乎有些混乱. 下面列出了示例代码(很抱歉,但是对于最小"最小示例,我莫名其妙地无法减小代码大小):

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):

队列类:

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;
};

生产者:

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;
};

消费者:

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;
};

最后是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;
}

这是结束正在等待条件变量的线程的正确方法还是有更好的方法?当前,队列需要知道程序是否已停止(我认为这会破坏组件的松散耦合),并且我需要在队列上显式调用stop(),这似乎不正确.

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.

此外,如果队列为空,应仅用作条件变量的条件变量现在代表另一个条件-如果程序已结束.如果我没记错的话,每次线程等待条件变量发生某种事件时,它还必须检查是否必须在继续执行之前停止线程(这似乎也是错误的).

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.

对条件变量附加多个条件(例如,队列中的任何内容或程序停止)对您来说是完全有效的.关键是要在wait返回时检查条件中的位.

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.

与其在Queue中没有标志来指示程序正在停止,您应该将标志视为我可以接受".这是一个更好的整体范例,并且在多线程环境中效果更好.

Instead of having a flag in Queue to indicate that the program is stopping you should think of the flag as "can I accept". This is a better overall paradigm and works better in a multi-threaded environment.

此外,如果有人调用了stop而不是让pop引发异常,则可以用bool try_pop(int &value)替换该方法,如果返回值,该方法将返回true,否则返回false .这样,调用方可以检查失败以查看队列是否已停止(添加bool is_stopped() const方法).尽管异常处理在这里起作用,但处理起来有点繁琐,而且在多线程程序中并不是真正的例外情况.

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. This way the caller can check on failure to see if the queue has been stopped (add a bool is_stopped() const method). Although exception handling works here it's a bit heavy handed and isn't really an exceptional case in a multi-threaded program.

这篇关于停止等待std :: condition_variable的C ++ 11 std :: threads的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆