下面是用两个线程,一个是生产者不断地产生数据,另一个则不断消耗数据。这个例子可以很好的演示生产者/消费者模型。由于C++在C++20之后才提供信号量类型,所以这里的测试环境是Qt5.9和VS2019。
Misc.h文件:

#pragma once#include <qsemaphore.h> #include <qthread.h> #include "qvector.h"class MData { public:MData(int imaxCount, int iblock);template<typename Func>void customer(Func func);template<typename Func>void producer(Func func);int blockSize() const;int count() const;quint8& operator[](int index);const quint8& operator[](int index) const;private:QSemaphore myAvailable; /* 多少可用的资源 */QSemaphore myLeft; /* 剩余多少空位置 */QVector<quint8> buffer;int maxCount;int eachSize; };class MProduce : public QThread {Q_OBJECTpublic:MProduce(MData* idata, QObject* parent = 0);void quit();private:void run() override;private:MData* data;int currBlock;bool running; };class MCustom : public QThread {Q_OBJECTpublic:MCustom(MData* idata, QObject* parent = 0);void quit();private:void run() override;private:MData* data;int currBlock;bool running; };
Misc.cpp文件:
MData::MData(int imaxCount, int iblock) :myAvailable(0), myLeft(imaxCount),buffer(imaxCount* iblock, 0) {maxCount = imaxCount;eachSize = iblock; }template<typename Func> bool MData::customer(Func func) {bool result = myAvailable.tryAcquire(1, 1000);if (result) /* 设置超时,这样在没有数据的时候线程可以退出 */{func(*this);myLeft.release();}return result; }template<typename Func> void MData::producer(Func func) {myLeft.acquire();func(*this);myAvailable.release(); }int MData::blockSize() const {return eachSize; }int MData::count() const {return maxCount; }quint8& MData::operator[](int index) {return buffer[index]; }const quint8& MData::operator[](int index) const {return buffer[index]; }MProduce::MProduce(MData* idata, QObject* parent) :QThread(parent) {data = idata;currBlock = 0;running = true; }void MProduce::quit() {running = false; }void MProduce::run() {while (running){data->producer([this](MData& d) {int size = d.blockSize();for (int i = 0; i < size; i++){d[currBlock * size + i] = qrand(); // 往里面填充随机数 }});currBlock = (currBlock + 1) % data->count();} }MCustom::MCustom(MData* idata, QObject* parent) :QThread(parent) {data = idata;currBlock = 0;running = true; }void MCustom::quit() {running = false; }void MCustom::run() {while (running){bool succeed = data->customer([this](MData& d) {int size = d.blockSize();qDebug() << u8"大小" << size << u8"已消耗";});if (succeed) // 消耗成功才向后移动数据 {currBlock = (currBlock + 1) % data->count();}} }
在主函数中使用。我是在QtWidget环境下测试的所以没有控制台输出而是用qDebug()输出数据:
void main() {MData* data = new MData(3, 1024); // 分配3个1KB的容器装数据MProduce produce(data, this);MCustom custom(data, this);qDebug() << u8"已启动";custom.start();produce.start();QThread::msleep(3000);qDebug() << u8"已停止生产者";produce.quit();produce.wait();QThread::msleep(3000);custom.quit();custom.wait();qDebug() << u8"已停止消费者";delete data; }
在VS2019的调试窗口的输出文本如下。从输出中可以看到在“停止生产者”之前输出的数据非常多,说明系统可以连续运行。而在“停止生产者”之后,只输出个别的数据就停止了,这符合停止生产者的设定。消费者只消耗生产者生产的剩余数据就停了,然后在超时后退出线程。
已启动 大小 1024 已消耗 ... 大小 1024 已消耗 大小 1024 已消耗 大小 1024 已消耗 已停止生产者 大小 1024 已消耗 线程 0xf7d8 已退出,返回值为 0 (0x0)。 线程 0x7450 已退出,返回值为 0 (0x0)。 已停止消费者