首页 > 其他分享 >生产者、消费者模式

生产者、消费者模式

时间:2022-08-26 14:33:32浏览次数:53  
标签:std product 消费者 生产者 模式 stk int fun ptr

是什么

生产者消费者模式(生产者消费者模式)是经典的线程同步案例,也称为有限缓冲问题。
生产者产生数据,但是数据不能超出缓冲区的限制,当缓冲区满时,停止生产。
消费者消费生产者产生的数据,当缓冲区为空时,停止消费。

能干什么

生产者消费者模式时一种设计思想,并不是一种固定的写法。就像23种设计模式一样,它所传递的解决问题的思路(思想、思维)。

例如,消息队列。消息队列就使用了生产者消费者模式的思想,
线程A(或系统A,生产者)产生消息,将消息入队,其他的线程(消费者)从消息队列中取出消息,并响应。

怎么干

为了方便展示生产者和消费之间的关系,特将队列换成栈去实现,因为入栈和出栈在同一端完成,可以根据出栈的顺序观察到生产者和消费者之间的关系。
以函数指针来表示一种消息。

std::stack<int (*)(const int x)> product_stk;

生产者pdoductor_fun的实现,在进入临界区时加锁,当栈(缓冲区)等于栈的预设大小时阻塞当前线程,wait等待消费者唤醒;否则生产消息,notify_all通知消费者可以消费。
用随机数表示产生的不同的消息(调用不同的函数)

void pdoductor_fun() {
    srand(time(NULL));
    while (1) {
        {
            std::unique_lock<std::mutex> ulk(mtx);
            while (product_stk.size() >= 20) {
                cond_full_var.wait(ulk);
            }
            if (rand() % 2 == 1) {
                product_stk.push(foo1);
            } else {
                product_stk.push(foo2);
            }
            ++global_num;
            cond_null_var.notify_all();
            std::cout << product_stk.size() << std::endl;
        }
        sleep(1);
    }
    return;
}

消费者cunsumer_fun当栈(缓冲区)空时,wait阻塞消费者线程,等待生产者唤醒;否则,取出栈中的消息,notify_all通知生产者可以生产。

void cunsumer_fun() {
    int (*ptr_fun)(const int x);
    int x;
    while (1) {
        {
            std::unique_lock<std::mutex> ulk(mtx);
            // lock_guard<std::mutex> lg(m);
            while (product_stk.empty()) {
                cond_null_var.wait(ulk);
            }
            x = global_num;
            ptr_fun = product_stk.top();
            product_stk.pop();
            cond_full_var.notify_all();
        }
        ptr_fun(x);
        sleep(3);
    }
    return;
}

完整代码如下,用sleep来控制消息产生的速度和消费的速度。虽然示例程序中有一个生产者、两个消费者,但消息产生的速度仍大于消费,可以观察出当栈满时消息不在产生。
注:linux下sleep的单位是秒s,windows下sleep的单位时毫秒ms。

#include <iostream>
#include <condition_variable>
#include <thread>
// #include <queue>
#include <stack>
#include <stdlib.h>
#include <unistd.h>
#include <mutex>

using namespace std;

int foo1(const int x);
int foo2(const int x);
void pdoductor_fun();
void cunsumer_fun();

const int STK_MAX_SIZE = 20;
std::stack<int (*)(const int x)> product_stk;
mutex mtx;
int global_num = 0;
std::condition_variable cond_null_var, cond_full_var;

int main() {

    std::thread productor_thread(pdoductor_fun);

    // std::cout << productor_thread.get_id() << std::endl;

    std::thread consumer_thread_1(cunsumer_fun);
    std::thread consumer_thread_2(cunsumer_fun);

    consumer_thread_1.join();
    consumer_thread_2.join();
    productor_thread.join();
    return 0;
}

void cunsumer_fun() {
    int (*ptr_fun)(const int x);
    int x;
    while (1) {
        {
            std::unique_lock<std::mutex> ulk(mtx);
            // lock_guard<std::mutex> lg(m);
            while (product_stk.empty()) {
                cond_null_var.wait(ulk);
            }
            x = global_num;
            ptr_fun = product_stk.top();
            product_stk.pop();
            cond_full_var.notify_all();
        }
        ptr_fun(x);
        sleep(3);
    }
    return;
}

void pdoductor_fun() {
    srand(time(NULL));
    while (1) {
        {
            std::unique_lock<std::mutex> ulk(mtx);
            while (product_stk.size() >= 20) {
                cond_full_var.wait(ulk);
            }
            if (rand() % 2 == 1) {
                product_stk.push(foo1);
            } else {
                product_stk.push(foo2);
            }
            ++global_num;
            cond_null_var.notify_all();
            std::cout << product_stk.size() << std::endl;
        }
        sleep(1);
    }
    return;
}

int foo1(const int x) {
    std::cout << "foo1 " << x << std::endl;
    return 1;
}

int foo2(const int x) {
    std::cout << "foo2 " << -x << std::endl;
    return 2;
}

cmake版本 3.16,源代码文件放在项目目录src文件夹下。CMakeLists.txt如下

cmake_minimum_required(VERSION 3.16)

project(product_consumer)

set(CXX_STANDARD c++11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# set(THREADS_PREFER_PTHREAD_FLAG ON)

find_package(Threads)

file(GLOB SRC_CC ${PROJECT_SOURCE_DIR}/src/*.cc)
file(GLOB SRC_H ${PROJECT_SOURCE_DIR}/src/*.h)

add_executable(product_consumer ${SRC_CC} ${SRC_H})

target_link_libraries(product_consumer PUBLIC ${CMAKE_THREAD_LIBS_INIT})

写在最后,是关于函数指针的问题。
假设有函数指针

int (*ptr_foo)();

在c中,ptr_foo既可以指向,返回值为int有参的函数,也可以指向返回值int无参的函数。

int foo1() { return 0; }
int foo2(const int x) { return 0; }
int foo3(const int x, char c) { return 0; }

ptr_foo = foo1;  // 正确
ptr_foo = foo1;  // 正确
ptr_foo = foo1;  // 正确

只能指向无参的函数指针应该这样写

int (*ptr_foo)(void);

而在c++中函数指针的参数列表需要和函数的参数列表相匹配

标签:std,product,消费者,生产者,模式,stk,int,fun,ptr
From: https://www.cnblogs.com/HanYG/p/16627091.html

相关文章

  • 第一章 模式识别的相关概念学习笔记
    1  相关概念1.1 什么是模式?可以被区分是否相似,存在于时间和空间中可观察的物体之中的信息。(模式不是事务本体,是从事物中获取的信息)1.2 模式的直观特性可观察性......
  • 【Account Kit】使用Authorization Code模式接入华为帐号,返回accessToken为空
    问题描述:使用AuthorizationCode模式接入华为帐号,返回AuthAccount的accessToken为空,并且没有返回uid解决方案:一般在静默登录的时候,需要在初始化HuaweiIdAuthParams对象......
  • 初识设计模式 - 单例模式
    简介一个类只允许创建一个对象(或实例),那么这个类就是一个单例类,这种设计模式称作单例设计模式(SingletonDesignPattern),简称单例模式。单例模式保证系统内存中只存在一个......
  • 01 Redis 三种搭建模式:主从模式-哨兵模式-高可用集群模式
    一、主从模式用域名指定主节点,当主节点宕机,改域名指向从节点缺点不知道什么时候挂掉,丢失数据,需要人工介入,运维24h待命 二、哨兵模式比主从模式,主要多了个哨兵,能自动......
  • Android学习笔记五(JAVA):创建新的Activity,启动新的Activity,管理任务之定义启动模式,从
    本篇笔记给QuizDemo新增一个HelpActivity,用户点击Help按钮,会跳转到HelpActivity屏幕,并选择是否查看答案。查看答案之后,返回到答题屏幕,但是如果已经看了答案,这一题的作答就......
  • 了解单例模式
    单例模式单例模式是什么?单例设计模式(SingletonDesignPattern)如果一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模......
  • 四、Spring Cloud Alibaba项目,测试模块之生产者与消费者
    1、模块说明生产者:service-order消费者:service-user说明:消费者user调用生产者order,调用订单服务 2、生产者pom文件<?xmlversion="1.0"encoding="UTF-8"?><proj......
  • python selenium使用无头模式执行用例
    什么是无头模式?HeadlessBrowser模式是浏览器的无界面状态,即在不打开浏览器界面的情况下使用浏览器。该模式的好处如下:1)可以加快web自动化测试的执行时间,对于web自动化......
  • 架构、框架、设计模式的定义和区别
    一、架构架构即软件架构,是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。软件体系结构是构建计算机软件实践的基础,简单来说,软件架构是一个系......
  • Javascript:设计模式-简单工厂模式
    工厂模式大体分为三类:简单工厂模式、工厂方法模式、抽象工厂模式。在我们日常的实现功能逻辑中,最基础的一种方法是这样的:有一个体育器材店,每一种类型的运动器材都有名称......