Qt线程同步-单生产者多消费者

公众号:Qt那些事儿

公众号

单生产者单消费者大家应该已经掌握了。上一篇使用的是QMutex跟QWaitCondition。

如果你的C++编译器版本比较高的话,那就可以使用C++11的 std::condition_variable了

代码

这次对比单生产者单消费者,我们多了mutex来进行消费者之间的同步。这样就可以写单生产者多消费者了。

这是代码中多的一个锁,用于同步消费者。

std::mutex mutexConsumer; //消费者用来同步用的

上代码,这是线程同步需要的变量

struct ItemRepository
{
    std::deque<int> itemQueue; //缓冲区
    const int MaxSize = 10; // 仓库所容纳的产品最大个数
    int itemCounterUse = 0; //消费者用了多少
    std::mutex mutex;// 互斥量,保护产品缓冲区
    std::mutex mutexConsumer; //消费者用来同步用的
    std::condition_variable bufferNotFull;     // 条件变量, 指产品仓库缓冲区不为满
    std::condition_variable bufferNotEmpty;    // 条件变量, 指产品仓库缓冲区不为空
}gItemRepository;   // 产品库全局变量,生产者和消费者操作该变量.

这里是实现代码

class Producer : public QThread
{
    Q_OBJECT
public:
    Producer(QObject *parent = NULL) : QThread(parent)
    {
    }
    // 生产 产品
    void ProduceItem(ItemRepository &itemRepo, int item)
    {
        std::unique_lock<std::mutex> lock(itemRepo.mutex);
        if (itemRepo.itemQueue.size() == itemRepo.MaxSize)
        {
            itemRepo.bufferNotFull.wait(lock);
            qDebug()<<"product has full"<<endl;
        }

//#ifdef Q_OS_WIN
//        Sleep(1000);
//#endif

        itemRepo.itemQueue.push_back(item);         // 仓库放入产品
        itemRepo.bufferNotEmpty.notify_all();  // 通知消费者仓库不为空
    }

    void run() override
    {
        for (int i = 1; i <= kItemsToProduce; i++)
        {
            ProduceItem(gItemRepository, i);
            {
                qDebug()<<"procut------------"<<i<<endl;
            }
        }
    }
};

class Consumer : public QThread
{
    Q_OBJECT
public:
    Consumer(QObject *parent = NULL) : QThread(parent)
    {
    }
    // 消费 产品
    int ConsumeItem(ItemRepository &itemRepo)
    {
        int data;
        std::unique_lock<std::mutex> lock(itemRepo.mutex);
        if (itemRepo.itemQueue.empty())
        {
            itemRepo.bufferNotEmpty.wait(lock);
        }
        data = itemRepo.itemQueue.front();
        itemRepo.itemQueue.pop_front();
        itemRepo.bufferNotFull.notify_all();
        return data;
    }

    void run() override
    {
        while (true)
        {
            int item = 0;
            std::unique_lock<std::mutex> lock(gItemRepository.mutexConsumer);  // 仓库产品消费计数器保持多线程互斥
            if (gItemRepository.itemCounterUse < kItemsToProduce)
            {
                item = ConsumeItem(gItemRepository);    // 消费产品
                gItemRepository.itemCounterUse++;  // 每消费一次进行计数器+1
            }

            lock.unlock();

            if (this->objectName() == "thread0")
            {
#ifdef Q_OS_WIN
                Sleep(2000);
#endif
            }
            else if (this->objectName() == "thread1")
            {
#ifdef Q_OS_WIN
                Sleep(3000);
#endif
            }
            else if (this->objectName() == "thread2")
            {
#ifdef Q_OS_WIN
                Sleep(2000);
#endif
            }
            qDebug()<<"Consumer------------"<<this->objectName()<<"-----"<<item<<endl;
        }
    }
};

有一点需要注意的是,消费的时候,应该在消费者的外面,所以我打的log是解锁之后sleep。这时候应该及时通知其它消费者线程赶紧调度。这样避免cpu的资源浪费。

稍微吐槽下,目前百度 Qt 单生产者多消费者的例子,十有八九是用Qt的信号量来实现的,可是写那篇文章的人也没有研究明白,那个例子有严重的bug。稍微变通下生产总数跟缓冲区总数就输出有问题了。慎用。

CSDN的文章就是相互抄袭啊。逃)

工程代码在这里

https://github.com/CryFeiFei/Qt_Teach/tree/master/Qt_Teach/Thread_Muliconsmer

公众号:Qt那些事儿

公众号

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值