基于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;
};
View Code

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)。
已停止消费者

 

posted @ 2025-03-11 11:30  兜尼完  阅读(51)  评论(0)    收藏  举报