基于Qt的信号量的使用
下面是用两个线程,一个是生产者不断地产生数据,另一个则不断消耗数据。这个例子可以很好的演示生产者/消费者模型。如果不是软件专业的不明白,可以搜一搜PV操作,做一做练习题很快就明白了。由于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_OBJECT public: MProduce(MData* idata, QObject* parent = 0); void quit(); private: void run() override; private: MData* data; int currBlock; bool running; }; class MCustom : public QThread { Q_OBJECT public: 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)。 已停止消费者