C/C++实现生产者消费者模式

Posted 停止摆烂,积极上进

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++实现生产者消费者模式相关的知识,希望对你有一定的参考价值。

文章目录

概要:本期主要学习使用WINAPI中的信号量机制来实现生产者消费者模式。

一、WINAPI中的信号量机制

1.创建信号量

2.释放信号量

3.等待信号量

二、生产者消费者模型

生产者消费者是一种计算机编程模型,用于解决多线程或并发编程中的同步问题。在这种模型中,生产者创建并向共享缓冲区中放置数据,而消费者则从缓冲区中取出数据并进行处理。生产者和消费者在并发执行的情况下,需要协调彼此的操作以避免竞态条件和数据不一致等问题。

生产者和消费者之间的共享缓冲区是一个数据结构,通常是一个队列或缓存区。生产者在将数据放入共享缓冲区之前必须检查其是否已满,而消费者在从中取出数据之前必须检查其是否为空。如果缓冲区已满,则生产者必须等待,直到有足够的空间可用。同样,如果缓冲区为空,则消费者必须等待,直到有数据可用。

生产者消费者模型的一个重要特点是,它可以将生产者和消费者分离开来,并允许它们以不同的速率执行。这种分离和异步执行的方式可以提高程序的效率和性能,同时保证数据的完整性和正确性。

三、代码实现

下面展示c/c++实现的代码:

#include <windows.h>
#include <iostream>
#include <queue>
#include <ctime>
#include <cstdlib>

using namespace std;

// 缓冲区大小
#define BUFFER_SIZE 10

// 生产者生产的次数
#define PRODUCER_COUNT 20

// 定义互斥量和信号量
HANDLE g_hMutex;
HANDLE g_hEmptySemaphore;
HANDLE g_hFullSemaphore;

// 缓冲区队列
queue<int> g_BufferQueue;

// 生产者线程函数
DWORD WINAPI ProducerThread(LPVOID lpParam) 
    for (int i = 0; i < PRODUCER_COUNT; ++i) 
        // 生成随机数
        int num = rand() % 100;
        // 等待空缓冲区
        WaitForSingleObject(g_hEmptySemaphore, INFINITE);
        // 获取互斥量
        WaitForSingleObject(g_hMutex, INFINITE);
        // 将数据添加到缓冲区队列中
        g_BufferQueue.push(num);
        cout << "Producer thread ID: " << GetCurrentThreadId() << " produced number: " << num << endl;
        // 释放互斥量
        ReleaseMutex(g_hMutex);
        // 发送信号表示缓冲区非空
        ReleaseSemaphore(g_hFullSemaphore, 1, NULL);
    
    return 0;


// 消费者线程函数
DWORD WINAPI ConsumerThread(LPVOID lpParam) 
    while (true) 
        // 等待非空缓冲区
        WaitForSingleObject(g_hFullSemaphore, INFINITE);
        // 获取互斥量
        WaitForSingleObject(g_hMutex, INFINITE);
        // 从缓冲区队列中取出数据并打印
        int num = g_BufferQueue.front();
        g_BufferQueue.pop();
        cout << "Consumer thread ID: " << GetCurrentThreadId() << " consumed number: " << num << endl;
        // 释放互斥量
        ReleaseMutex(g_hMutex);
        // 发送信号表示缓冲区非满
        ReleaseSemaphore(g_hEmptySemaphore, 1, NULL);
        // 如果缓冲区为空,则退出循环
        if (g_BufferQueue.empty()) 
            break;
        
    
    return 0;


int main() 
    // 初始化随机数生成器
    srand(time(NULL));

    // 创建互斥量和信号量
    g_hMutex = CreateMutex(NULL, FALSE, NULL);
    g_hEmptySemaphore = CreateSemaphore(NULL, BUFFER_SIZE, BUFFER_SIZE, NULL);
    g_hFullSemaphore = CreateSemaphore(NULL, 0, BUFFER_SIZE, NULL);

    // 创建生产者和消费者线程
    HANDLE hProducerThread = CreateThread(NULL, 0, &ProducerThread, NULL, 0, NULL);
    HANDLE hConsumerThread1 = CreateThread(NULL, 0, &ConsumerThread, NULL, 0, NULL);
    HANDLE hConsumerThread2 = CreateThread(NULL, 0, &ConsumerThread, NULL, 0, NULL);

    // 等待线程执行完毕
    WaitForSingleObject(hProducerThread, INFINITE);
    WaitForSingleObject(hConsumerThread1, INFINITE);
   
   //清除WINAPI句柄
   CloseHandle(g_hMutex);
   CloseHandle(g_hEmptySemaphore );
   CloseHandle(g_hFullSemaphore );
   
   return 0;

结尾

本期对于生产者消费者模式的学习就到这,下期再会:)

实现生产者与消费者模式

实现生产者与消费者模式

 

目录

生产者与消费者模式
实现

 

 

 

生产者与消费者模式

 

什么是生产者消费者模式

生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

这个阻塞队列就是用来给生产者和消费者解耦的。纵观大多数设计模式,都会找一个第三者出来进行解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类。在学习一些设计模式的过程中,如果先找到这个模式的第三者,能帮助我们快速熟悉一个设计模式。

 

技术分享图片

 

 

 

 

 为什么要使用生产者消费者模式

 主要是为了解耦与并发

 

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

 

 

支持并发
在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的线程,在多线程开发中,如果生产者处理速度很快,而消费者的速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。

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

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

 

 

 

生产者消费者模式标准

1、生产者仅仅在仓储未满时候生产,仓满则停止生产。
2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。
3、当消费者发现仓储没产品可消费时候会通知生产者生产。
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。

 

 

 

 

 

实现

这里只是简单实现了

消费者仅仅在仓储有产品时候才能消费,仓空则等待

生产者在生产出可消费产品时候,应该通知等待的消费者去消费

q.task_done()与q.join()实现了这两个功能。

 

 

Producter方法:生产者制造数据

q.put()方法:相当于生产者把数据放入缓冲区

q.put()方法:相当于消费者把数据取出缓存区

Comsumer方法:消费者处理数据

 

 

程序示例

import time
import queue,threading

q=queue.Queue()


#生产者
def Producter(name):

    n=1
    while 1:
        time.sleep(1)
        print(‘已经生产出来%d个包子‘ % (n))
        # 将数据发给队列
        q.put(n)
        n+=1
        #通知队列,这里是通知队列已经发完数据
        q.task_done()


#消费者
def Comsumer(name):
    while 1:
        time.sleep(1)
        # 等待接收到通知才继续往下执行
        print(‘%s在等待包子‘ %name)
        q.join()
        #从队列里面取值
        count = q.get()
        print(‘%s吃到第%d个包子‘ % (name, count))



c1=threading.Thread(target=Producter,args=(‘C‘))
p1=threading.Thread(target=Comsumer,args=(‘A‘))
p2=threading.Thread(target=Comsumer,args=(‘B‘))

c1.start()
p1.start()
p2.start()

  

 

演示结果

 技术分享图片

 






以上是关于C/C++实现生产者消费者模式的主要内容,如果未能解决你的问题,请参考以下文章

用C语言实现--生产者与消费者的问题(PV操作)

C语言如何终止线程?

C语言实现生产者消费者进程同步问题?

用C语言实现PV操作生产者消费者关系

C语言之简单使用互斥锁条件锁实现生产者消费者模型操作

go语言中实现生产者-消费者模式有哪些方法呢