Qt示例多线程之生产者消费者模型

发布于:2025-08-07 ⋅ 阅读:(20) ⋅ 点赞:(0)

下面是一个使用Qt的 QWaitCondition 的完整示例工程,展示如何实现生产者-消费者模型。生产者生成数据,消费者在数据可用时处理数据:

// main.cpp
#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QDebug>
#include <cstdlib>

// 共享数据结构
class SharedData {
public:
    static const int BUFFER_SIZE = 5;
    
    SharedData() : usedSlots(0) {}
    
    int buffer[BUFFER_SIZE];
    int usedSlots;  // 当前缓冲区中的数据数量
    int produceIndex = 0;
    int consumeIndex = 0;
    
    QMutex mutex;
    QWaitCondition bufferFull;  // 缓冲区满时等待的条件
    QWaitCondition bufferEmpty; // 缓冲区空时等待的条件
};

// 生产者线程
class Producer : public QThread {
public:
    Producer(SharedData* data, QObject* parent = nullptr)
        : QThread(parent), sharedData(data) {}
    
protected:
    void run() override {
        for (int i = 0; i < 15; ++i) { // 生产15个数字
            sharedData->mutex.lock();
            
            // 如果缓冲区满则等待
            while (sharedData->usedSlots == SharedData::BUFFER_SIZE) {
                qDebug() << ">>>>> 缓冲区满,生产者等待...";
                sharedData->bufferFull.wait(&sharedData->mutex);
            }
            
            // 生产数据
            int num = rand() % 100;
            sharedData->buffer[sharedData->produceIndex] = num;
            sharedData->produceIndex = (sharedData->produceIndex + 1) % SharedData::BUFFER_SIZE;
            sharedData->usedSlots++;
            
            qDebug() << "生产者添加: " << num << " | 缓冲区大小:" << sharedData->usedSlots;
            
            // 通知消费者缓冲区不为空
            sharedData->bufferEmpty.wakeAll();
            sharedData->mutex.unlock();
            
            msleep(rand() % 300); // 模拟生产时间
        }
    }

private:
    SharedData* sharedData;
};

// 消费者线程
class Consumer : public QThread {
public:
    Consumer(SharedData* data, QObject* parent = nullptr)
        : QThread(parent), sharedData(data) {}
    
protected:
    void run() override {
        for (int i = 0; i < 15; ++i) { // 消费15个数字
            sharedData->mutex.lock();
            
            // 如果缓冲区空则等待
            while (sharedData->usedSlots == 0) {
                qDebug() << "<<<<< 缓冲区空,消费者等待...";
                sharedData->bufferEmpty.wait(&sharedData->mutex);
            }
            
            // 消费数据
            int num = sharedData->buffer[sharedData->consumeIndex];
            sharedData->consumeIndex = (sharedData->consumeIndex + 1) % SharedData::BUFFER_SIZE;
            sharedData->usedSlots--;
            
            qDebug() << "消费者获取: " << num << " | 缓冲区大小:" << sharedData->usedSlots;
            
            // 通知生产者缓冲区不满
            sharedData->bufferFull.wakeAll();
            sharedData->mutex.unlock();
            
            msleep(rand() % 300); // 模拟消费时间
        }
    }

private:
    SharedData* sharedData;
};

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    qDebug() << "===== 开始生产者-消费者示例 =====";
    
    // 创建共享数据
    SharedData sharedData;
    
    // 创建并启动线程
    Producer producer(&sharedData);
    Consumer consumer(&sharedData);
    producer.start();
    consumer.start();
    
    // 等待线程完成
    producer.wait();
    consumer.wait();
    
    qDebug() << "===== 示例结束 =====";
    return 0;
}

代码说明:

  1. 共享数据结构 (SharedData):

    • 环形缓冲区(大小为5)
    • QMutex 保护共享资源
    • 两个 QWaitCondition:
      • bufferFull:当缓冲区满时,生产者等待此条件
      • bufferEmpty:当缓冲区空时,消费者等待此条件
  2. 生产者线程:

    • 当缓冲区满时调用 bufferFull.wait() 挂起
    • 向缓冲区添加随机数字(0-99)
    • 添加数据后通过 bufferEmpty.wakeAll() 唤醒消费者
  3. 消费者线程:

    • 当缓冲区空时调用 bufferEmpty.wait() 挂起
    • 从缓冲区取出数字处理
    • 取出数据后通过 bufferFull.wakeAll() 唤醒生产者
  4. 同步机制:

    • 使用 while循环检查条件(避免虚假唤醒)
    • mutex保护所有共享数据的访问
    • wait() 自动释放互斥锁,唤醒时重新获取

运行结果示例:

===== 开始生产者-消费者示例 =====
生产者添加:  67 | 缓冲区大小: 1
消费者获取:  67 | 缓冲区大小: 0
生产者添加:  86 | 缓冲区大小: 1
生产者添加:  44 | 缓冲区大小: 2
<<<<< 缓冲区空,消费者等待...
消费者获取:  86 | 缓冲区大小: 1
生产者添加:  83  | 缓冲区大小: 2
<<<<< 缓冲区空,消费者等待...
消费者获取:  44 | 缓冲区大小: 1
>>>>> 缓冲区满,生产者等待...
消费者获取:  83 | 缓冲区大小: 0
... (更多交替执行的日志) ...
===== 示例结束 =====

编译方法:

  1. 创建Qt控制台项目 (CONFIG += console)
  2. 复制上述代码到 main.cpp
  3. .pro 文件中添加:
    QT += core concurrent
    CONFIG += c++11
    
  4. 编译运行(确保安装了Qt开发环境)

关键点:

  • QWaitCondition 必须与 QMutex 配合使用
  • 通过两个独立的condition避免了"死锁"风险
  • while循环检查条件保证唤醒后重新验证状态
  • 环形缓冲区实现了高效的内存复用

这个示例展示了如何使用Qt的同步原语实现线程间安全通信,可直接用于实际项目中任务队列、数据流水线等场景。


网站公告

今日签到

点亮在社区的每一天
去签到