qt 生产者-消费者模型学习

我参考的网址:https://mp.weixin.qq.com/s/wh9RLbDwP1AoNy50i4WEdg
https://www.cnblogs.com/chentingk/p/6497107.html

生产者-消费者模型
在这里插入图片描述
生产者消费者模型的实现
生产者是一堆线程,消费者是另一堆线程,内存缓冲区可以使用List数组队列,数据类型只需要定义一个简单的类就好。关键是如何处理多线程之间的协作。这其实也是多线程通信的一个范例。
  在这个模型中,最关键就是内存缓冲区为空的时候消费者必须等待,而内存缓冲区满的时候,生产者必须等待。其他时候可以是个动态平衡。值得注意的是多线程对临界区资源的操作时候必须保证在读写中只能存在一个线程,所以需要设计锁的策略。

qt中相关的库
条件变量QWaitCondition
Qt里面叫等待条件,Linux下叫条件变量,统一都称呼为条件变量QWaitCondition 允许线程在某些情况发生时唤醒另外的线程。一个或多个线程可以阻塞等待QWaitCondition ,用wakeOne()或wakeAll()设置一个条件。wakeOne()随机唤醒一个,wakeAll()唤醒所有。
QWaitCondition ()
bool wait ( QMutex * lockedMutex, unsigned long time = ULONG_MAX )
释放lockedMutex并等待等待条件。lockedMutex最初必须由调用线程锁定。如果lockedMutex未处于锁定状态,则行为未定义。如果lockedMutex是递归互斥体,则此函数将立即返回。lockedMutex将被解锁,调用线程将阻塞,直到满足以下任一条件:
1、另一个线程使用wakeOne()或wakeAll()发出信号。在这种情况下,此函数将返回true。
2、time毫秒已过。如果time是ULONG_MAX(默认值),那么等待永远不会超时(必须用信号通知事件)。如果等待超时,此函数将返回false。
lockedMutex将返回到相同的锁定状态。提供此函数是为了允许原子从锁定状态转换到等待状态。

bool wait ( QReadWriteLock * readWriteLock, unsigned long time = ULONG_MAX )
释放lockedReadWriteLock并等待等待条件。lockedReadWriteLock最初必须由调用线程锁定。如果lockedReadWriteLock未处于锁定状态,则此函数将立即返回。lockedReadWriteLock不能递归锁定,否则此函数将无法正确释放锁。lockedReadWriteLock将被解锁,调用线程将阻塞,直到满足以下任一条件:
1、另一个线程使用wakeOne()或wakeAll()发出信号。在这种情况下,此函数将返回true。
2、time毫秒已过。如果time是ULONG_MAX(默认值),那么等待永远不会超时(必须用信号通知事件)。如果等待超时,此函数将返回false。
lockedReadWriteLock将返回到相同的锁定状态。提供此函数是为了允许原子从锁定状态转换到等待状态。

void wakeOne ()
唤醒一个等待条件的线程。被唤醒的线程取决于操作系统的调度策略,无法控制或预测
如果要唤醒特定线程,解决方案通常是使用不同的等待条件,并让不同的线程在不同的条件下等待

void wakeAll ()
唤醒等待条件的所有线程。线程的唤醒顺序取决于操作系统的调度策略,无法控制或预测。

头文件声明: #include
等待条件声明: QWaitCondtion m_WaitCondition;
等待条件等待: m_WaitConditon.wait(&m_muxtex, time);
等待条件唤醒: m_WaitCondition.wakeAll();

在经典的生产者-消费者场合中,生产者首先必须检查缓冲是否已满,如果缓冲区已满,线程停下来等待 notfull条件。如果没有满,在缓冲中生产数据,激活条件 notempty。使用mutex来保护对buffer的访问。QWaitCondition::wait() 接收一个mutex作为参数,mutex被调用线程初始化为锁定状态。在线程进入休眠状态之前,mutex会被解锁。而当线程被唤醒时,mutex会处于锁定状态,从锁定状态到等待状态的转换是原子操作。当程序开始运行时,只有生产者可以工作,消费者被阻塞等待notempty条件,一旦生产者在缓冲中放入一个字节,notempty条件被激发,消费者线程于是被唤醒。
notfull和notempty是自定义的等待条件

内存缓冲区,虽然buffer有大小限制,但实现的是循环的

class producons
{
	public:
	    int buffer[BUFFER_SIZE];    /*数据*/
	    QMutex lock;                //互斥锁
	    int readpos,writepos;       //读写位置
	    QWaitCondition notempty;   //条件变量  没有空间
	    QWaitCondition notfull;     //条件变量  没有货物
	    producons()
	    {
	        readpos = writepos = 0;
	    }
	};
	producons buffer;    //内存缓冲区对象

生产者线程,只修改writepos

class Producor:public QThread
{
public:
    void run();
    void put(producons * prod,int data);
};


void Producor::run()
{
    int n;
    for(n = 0;n<5;n++)
    {
        qDebug()<<"生产者睡眠 1s...";
        sleep(1);
        qDebug()<<"生产信息:" << n;
        put(&buffer, n);
    }
    for(n=5; n<10; n++)
    {
        qDebug()<<"生产者睡眠 3s...";
        sleep(3);
        qDebug()<<"生产信息:"<< n;
        put(&buffer,n);
    }
    put(&buffer, -1);
    qDebug()<<"结束生产者!\n";
    return;
}
void Producor::put(producons *prod, int data)
{
    prod->lock.lock();

    //这句话相当于生产者不断的生产,writepos就是生产者当前正在生产的位置,readpos就是最后一个消费者,现在就是之前生产的还没消耗,writepos跟在readpos后面,buffer是一个循环的缓冲区
    while((prod->writepos + 1)%BUFFER_SIZE == prod->readpos)
    {
        qDebug()<<"生产者等待生产,直到buffer有空位置";
        prod->notfull.wait(&prod->lock);
    }
    //将数据写入到buffer里面去
    prod->buffer[prod->writepos] = data;
    prod->writepos++;
    if(prod->writepos >= BUFFER_SIZE)
        prod->writepos = 0;
    //仓库已满,唤醒所有线程,等待消费者消费,该线程被阻塞
    prod->notempty.wakeAll();
    prod->lock.unlock();
}

消费者线程,只修改readpos

class Consumer:public QThread
{
public:
    void run();
    int get(producons *prod);
};

void Consumer::run()
{
    int d = 0;
    while(1)
    {
        qDebug()<<"消费者睡眠 2s...";
        sleep(2);
        d = get(&buffer);
        qDebug()<<"读取信息:"<< d;
        if(d == -1) break;
    }
    qDebug()<<"结束消费者!";
    return;
}

int Consumer::get(producons *prod)
{
    int data;
    prod->lock.lock();      //加锁
    
    //此时表示buffer的这个位置既要读取,又要写入,即消费者没有东西可以消费,需等待生产者生产
    while(prod->writepos == prod->readpos)
    {
        qDebug()<<"消费者等待,直到buffer有消息\n";
        prod->notempty.wait(&prod->lock);
    }
    //读取buffer里面的消息
    data = prod->buffer[prod->readpos];
    prod->readpos++;

    if(prod->readpos >=BUFFER_SIZE)
        prod->readpos = 0;
    //触发非满条件变量 告诉生产者可以生产
    prod->notfull.wakeAll();
    prod->lock.unlock();

    return data;
}
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 下面是一个使用 Qt 线程池实现生产者消费者模型的简单示例: ```cpp #include <QtCore> #include <iostream> class Producer : public QObject { Q_OBJECT public: Producer(QThreadPool *pool, QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_pool(pool), m_mutex(mutex), m_cond(cond), m_data(data) { m_count = 0; } public slots: void produce() { int num = qrand() % 100; QRunnable *task = new Task(num, m_mutex, m_cond, m_data); m_pool->start(task); m_count++; std::cout << "Produced " << num << std::endl; m_cond->wakeAll(); } int count() const { return m_count; } private: QThreadPool *m_pool; QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; int m_count; }; class Consumer : public QObject { Q_OBJECT public: Consumer(QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_mutex(mutex), m_cond(cond), m_data(data) { } public slots: void consume() { m_mutex->lock(); while (m_data->isEmpty()) { m_cond->wait(m_mutex); } int num = m_data->takeFirst(); std::cout << "Consumed " << num << std::endl; m_mutex->unlock(); } private: QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; }; class Task : public QRunnable { public: Task(int num, QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_num(num), m_mutex(mutex), m_cond(cond), m_data(data) { } void run() { m_mutex->lock(); while (m_data->size() >= 10) { m_cond->wait(m_mutex); } m_data->append(m_num); m_mutex->unlock(); } private: int m_num; QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; }; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); qsrand(QTime::currentTime().msec()); QThreadPool pool; pool.setMaxThreadCount(5); QMutex mutex; QWaitCondition cond; QList<int> data; Producer producer(&pool, &mutex, &cond, &data); Consumer consumer(&mutex, &cond, &data); QObject::connect(&producer, &Producer::produce, &consumer, &Consumer::consume); QTimer timer; QObject::connect(&timer, &QTimer::timeout, &producer, &Producer::produce); timer.start(1000); QTimer countTimer; QObject::connect(&countTimer, &QTimer::timeout, [&producer]() { std::cout << "Produced " << producer.count() << " items" << std::endl; }); countTimer.start(5000); return app.exec(); } ``` 在上面的示例中,`Producer` 和 `Consumer` 类继承自 `QObject`,并使用 Qt 信号和槽机制进行通信。`Producer` 类的 `produce()` ### 回答2: Qt线程池可以很好地实现生产者消费者模型生产者消费者模型主要是通过一个共享的缓冲区来实现生产者生产数据,消费者消费数据的过程。下面是一个使用Qt线程池实现生产者消费者模型的示例: 1. 定义一个数据结构,用于存储生产者消费者共享的数据。这个数据结构可以是一个队列或者一个列表。 2. 创建一个Qt线程池,并设置线程池的最大线程数。 3. 创建生产者线程和消费者线程,并将它们添加到线程池中。 4. 在生产者线程中,生产数据并将数据添加到共享的数据结构中。可以使用Qt的信号槽机制,在生产者线程中发送信号,将数据发送给消费者线程。 5. 在消费者线程中,从共享的数据结构中获取数据并进行消费。可以使用Qt的信号槽机制,在消费者线程中接收信号,并处理收到的数据。 6. 在主线程中,等待生产者线程和消费者线程完成工作,并关闭线程池。 通过使用Qt线程池,可以方便地管理多个线程,避免手动管理线程的创建和销毁,从而简化了生产者消费者模型的实现。此外,Qt的信号槽机制可以方便地实现线程间的通信,从而实现生产者消费者之间的数据传递。 ### 回答3: Qt是一个跨平台的C++应用程序开发框架,它提供了丰富的类库和工具,其中也包括线程池的实现。在Qt中,可以使用QThreadPool类来创建和管理线程池。 生产者消费者模型是一种常见的多线程编程模型,其中有一组线程作为生产者,负责生成数据,另一组线程作为消费者,负责处理这些数据。线程池可以很好地支持这种模型,并提供了以下步骤来实现: 1. 创建一个线程池对象:使用QThreadPool类创建一个线程池对象,设置最大线程数、线程闲置时间等属性。 2. 创建生产者线程:通过继承QRunnable类,实现自己的生产者线程类。在类中重写run()函数,在其中完成需要生产的数据的生成。 3. 创建消费者线程:同样通过继承QRunnable类,实现自己的消费者线程类。在类中重写run()函数,在其中完成对生产者生成的数据的处理。 4. 将任务添加到线程池:使用QThreadPool的start()函数将生产者消费者线程对象添加到线程池中,线程池会自动分配线程去运行这些任务。 5. 等待线程池完成任务:可以使用QThreadPool的waitForDone()函数来等待线程池中的所有任务完成,确保所有生产者消费者线程都执行完毕。 通过以上步骤,我们可以在Qt中实现简单的生产者消费者模型。线程池可以很好地管理线程的创建和销毁,提高线程利用率和系统的性能。同时,Qt的线程池也提供了一些其他的功能,比如任务优先级和取消线程等,可以根据实际需求来灵活调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值