【问题标题】:In a simple C++ 11 four-thread program, my two consumer threads are not returning if I comment stdout print lines在一个简单的 C++ 11 四线程程序中,如果我注释 stdout 打印行,我的两个使用者线程不会返回
【发布时间】:2024-01-08 05:49:01
【问题描述】:

我正在学习使用 C++ 11 的同步原语。我必须为模板类编写这些方法,该模板类是一个 FIFO 队列,其最大元素数在构造时声明。

有两个线程将项目推入所述队列,两个线程检索它们。它们通过使用两个条件变量进行同步,以确保消费者线程仅在队列不为空时弹出项目,而生产者线程仅在队列未满时推送新项目。队列具有打开/关闭状态,该状态用作两个条件变量的 wait() 调用中的附加条件。当队列关闭时,线程应该返回而不执行任何操作。

// main.cpp
#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[]){
    BlockingQueue<int> bq(10);
    int sum1=0, sum2=0;

    std::thread c1([&bq,&sum1](){
        int i;
        while(bq.get(i)) sum1+=i;
    });
    std::thread c2([&bq,&sum2](){
        int i;
        while(bq.get(i)) sum2+=i;
    });
    std::thread p1([&bq](){
    for(int i=0;i<1000;i+=2) bq.put(i);
    });
    std::thread p2([&bq](){
    for(int i=0;i<1000;i+=2) bq.put(i+1);
    });
    p1.join();
    std::cout<<"p1 thread returned."<<std::endl;
    p2.join();
    std::cout<<"p2 thread returned."<<std::endl;
    bq.close();
    c1.join();
    std::cout<<"c1 thread returned."<<std::endl;
    c2.join();
    std::cout<<"c2 thread returned."<<std::endl;
    std::cout<<"sum1: "<<sum1<<std::endl;
    std::cout<<"sum2: "<<sum2<<std::endl;
    std::cout<<"total: "<<sum1+sum2<<std::endl;
    return 0;
}

这是我创建的类:

// BlockingQueue.h
#include "stdafx.h"

template<class T> class BlockingQueue
{
    std::mutex t_queue_mutex;
    std::queue<T> t_queue;
    int t_queue_cap_value;
    bool isQueueOpen;
    std::condition_variable put_condition;
    std::condition_variable get_condition;
public:
    BlockingQueue(int N);
    ~BlockingQueue(void);
    bool put(T t_item);
    bool get(T &t_item);
    bool isOpen();
    bool isFull();
    bool isEmpty();
    void close();
};

// BlockinQueue.cpp
#include "BlockingQueue.h"
#include "stdafx.h"

template <class T> BlockingQueue<T>::BlockingQueue(int N)
{
    t_queue_cap_value=N;
    isQueueOpen=true;
    std::cout<<"Rejoice! A bq has been created!"<<std::endl;
}

template <class T> BlockingQueue<T>::~BlockingQueue(void)
{
}

template <class T> bool BlockingQueue<T>::isFull(){
    if(t_queue_cap_value==t_queue.size())
        return true;
    else
        return false;
}

template <class T> bool BlockingQueue<T>::isOpen(){
    return isQueueOpen;
}

template <class T> void BlockingQueue<T>::close(){
    isQueueOpen=false;
}

/* get method */
template <class T> bool BlockingQueue<T>::get(T &t_item){
    bool exitThreadStatus=false;

    if(!isOpen()){
        put_condition.notify_all();
        return false;
    }
    std::unique_lock<std::mutex> ul(t_queue_mutex);
    get_condition.wait(ul, [this](){
        //std::cout<<"Getter thread with get_id()="<<std::this_thread::get_id()<<" is waiting. isOpen()="<<isOpen()<<" and t_queue.empty()="<<t_queue.empty()<<std::endl;
        if(!isOpen())
            return true;
        else
            return !t_queue.empty();
    }); 
    if(isOpen()){
        exitThreadStatus=true;
        t_item=t_queue.front();
        t_queue.pop();
    }
    std::cout<<"Extracted "<<t_item<<". After pop size()="<<t_queue.size()<<std::endl;
    put_condition.notify_all();
    return exitThreadStatus;
}

/* put method */
template <class T> bool BlockingQueue<T>::put(T t_item){
    bool exitThreadStatus=false;

    if(!isOpen()){
        get_condition.notify_all();
        return false;
    }
    std::unique_lock<std::mutex> ul(t_queue_mutex);
    put_condition.wait(ul, [this](){
        if(!isOpen())
            return true;
        else
            return !isFull();
    }); 
        if(isOpen()){
        exitThreadStatus=true;
        t_queue.push(t_item);
    }
    std::cout<<"Inserting "<<t_item<<". After push size()="<<t_queue.size()<<std::endl;
    get_condition.notify_all();
    return exitThreadStatus;
}

template class BlockingQueue<int>;

每当我将 get() 和 put() 中的两个 std::cout 行未注释时,它似乎工作正常,得到以下输出(如预期的那样):

Inserting 998. After push size()=2
Extracted 997. After pop size()=1
p1 thread returned.
Inserting 999. After push size()=2
Extracted 998. After pop size()=1
p2 thread returned.
Extracted 999. After pop size()=0
Extracted 998. After pop size()=0
c1 thread returned.
c2 thread returned.
sum1: 250000
sum2: 249500
total: 499500

如果我改为注释 cout 行,两个收集线程似乎永远不会返回,我无法理解我的代码有什么问题。有人有线索吗?谢谢!

带有注释的 cout 行的输出:

Rejoice! A bq has been created!
p1 thread returned.
p2 thread returned.

【问题讨论】:

    标签: c++ multithreading c++11 mutex condition-variable


    【解决方案1】:

    尝试将 get_condition.notify_all() 和 put_condition.notify_all() 添加到 close() 中。

    据我所知,如果在调用 close() 时线程位于 get() 内部的 get_condition.wait() 中,它将永远处于等待状态。尽管文档确实提到了“虚假唤醒”,但我不知道为什么这适用于 cout 语句。

    【讨论】:

    • 行得通!非常感谢。应该通过使用唤醒条件作为第二个参数以传递给条件变量 wake() 调用的 lambda 表达式的形式来处理虚假唤醒。
    最近更新 更多