Qt笔记——QSemaphore处理生产者/消费者模式

目录

生产者消费者模式

手册选读

main.cpp

.pro

运行结果


生产者消费者模式

转载:https://blog.csdn.net/u011109589/article/details/80519863

简介

在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。

    单单抽象出生产者和消费者,还够不上是生产者/消费者模式。该模式还需要有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,而消费者从缓冲区取出数据。大概的结构如下图。

   为了不至于太抽象,我们举一个寄信的例子(虽说这年头寄信已经不时兴,但这个例子还是比较贴切的)。假设你要寄一封平信,大致过程如下:

    1、你把信写好——相当于生产者制造数据

    2、你把信放入邮筒——相当于生产者把数据放入缓冲区

    3、邮递员把信从邮筒取出——相当于消费者把数据取出缓冲区

    4、邮递员把信拿去邮局做相应的处理——相当于消费者处理数据

   优点

   可能有同学会问了:这个缓冲区有什么用捏?为什么不让生产者直接调用消费者的某个函数,直接把数据传递过去?搞出这么一个缓冲区作甚?

    其实这里面是大有讲究的,大概有如下一些好处。

   解耦

    假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。

    接着上述的例子,如果不使用邮筒(也就是缓冲区),你必须得把信直接交给邮递员。有同学会说,直接给邮递员不是挺简单的嘛?其实不简单,你必须得认识谁是邮递员,才能把信给他(光凭身上穿的制服,万一有人假冒,就惨了)。这就产生和你和邮递员之间的依赖(相当于生产者和消费者的强耦合)。万一哪天邮递员换人了,你还要重新认识一下(相当于消费者变化导致修改生产者代码)。而邮筒相对来说比较固定,你依赖它的成本就比较低(相当于和缓冲区之间的弱耦合)。

    ◇支持并发(concurrency)

    生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者就会白白糟蹋大好时光。

    使用了生产者/消费者模式之后,生产者和消费者可以是两个独立的并发主体(常见并发类型有进程和线程两种,后面的帖子会讲两种并发类型下的应用)。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。

    其实当初这个模式,主要就是用来处理并发问题的。

    从寄信的例子来看。如果没有邮筒,你得拿着信傻站在路口等邮递员过来收(相当于生产者阻塞);又或者邮递员得挨家挨户问,谁要寄信(相当于消费者轮询)。不管是哪种方法,都挺土的。

    ◇支持忙闲不均

    缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。

    为了充分复用,我们再拿寄信的例子来说事。假设邮递员一次只能带走1000封信。万一某次碰上情人节(也可能是圣诞节)送贺卡,需要寄出去的信超过1000封,这时候邮筒这个缓冲区就派上用场了。邮递员把来不及带走的信暂存在邮筒中,等下次过来时再拿走。

    费了这么多口水,希望原先不太了解生产者/消费者模式的同学能够明白它是怎么一回事。接下来说说数据单元。

   数据单元

    何谓数据单元捏?简单地说,每次生产者放到缓冲区的,就是一个数据单元;每次消费者从缓冲区取出的,也是一个数据单元。对于前一个帖子中寄信的例子,我们可以把每一封单独的信件看成是一个数据单元。

    不过光这么介绍,太过于简单,无助于大伙儿分析出这玩意儿。所以,后面咱们来看一下数据单元需要具备哪些特性。搞明白这些特性之后,就容易从复杂的业务逻辑中分析出适合做数据单元的东西了。

 手册选读

https://doc.qt.io/qt-5/qsemaphore.html#details

信号量是互斥量的概括。尽管互斥锁只能被锁定一次,但是有可能多次获取信号量。信号量通常用于保护一定数量的相同资源。

信号量支持两个基本操作,acquire()和release()

main.cpp

#include <QCoreApplication>
#include <QSemaphore>
#include <QThread>
#include <stdio.h>

const int DataSize = 1000;
const int BufferSize = 80;

int buffer[BufferSize];//生产者向buffer中写入数据,消费者读取前者的数据,可以减少信号量带来的开销
QSemaphore freeBytes(BufferSize);//控制被生产者填充的缓冲区部分
QSemaphore usedBytes(0); //控制可被消费者读取的缓冲区部分

//生产者
class Producer:public QThread{
public:
    Producer();
    void run();
};

void Producer::run(){
    for(int i=0;i<DataSize;i++){
        freeBytes.acquire();//获取空闲的数据单元
        buffer[i%BufferSize]=(i%BufferSize);//使用当前缓冲区单元序号填写这个缓冲区单元
        usedBytes.release();//释放资源
    }
}

Producer::Producer(){

}

//消费者
class Consumer:public QThread{
public:
    Consumer();
    void run();
};

void Consumer::run(){
    for(int i=0;i<DataSize;i++){
        usedBytes.acquire();//消费者获取可被读取的数据单元
        fprintf(stderr,"%d",buffer[i%BufferSize]);//当消费者成功获取数据单元,打印下来
        if(i%16==0&&i!=0){
            fprintf(stderr,"\n");
        }
        freeBytes.release();//释放其数据单元供给下次生产者填充
    }
    fprintf(stderr,"\n");
}

Consumer::Consumer(){

}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Producer producer;
    Consumer consumer;

    //启动消费者和生产者的线程
    producer.start();
    consumer.start();

    //等待消费者执行完毕后退出
    producer.wait();
    consumer.wait();

    return a.exec();
}

.pro

QT += core
QT -= gui

CONFIG += c++11

TARGET = Semaphore
CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

运行结果

发布了250 篇原创文章 · 获赞 267 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/qq_41895747/article/details/104102307