生产者消费者模型(多线程工作)

news2024/11/26 20:38:18

目录

1.模型前提

2.阻塞队列(消费场所)

3. 实验

4.有关效率


1.模型前提

以单生产者对单消费者为例子:

前提一:有一个缓冲区作为消费场所。

前提二:有两种功能不同的线程分别具有消费与生产的能力。

前提三:生产者与生产者之间有互斥的关系,消费者与消费者之间有互斥的关系,生产者与消费者之间同时具有互斥与同步的关系。


2.阻塞队列(消费场所)

阻塞队列与普通队列最大的不同是:当队列满时生产者不能继续生产,当队列空时消费者不能继续消费。


3. 实验

来展示一份多线程工作遵循生产者消费者模型的代码。

部分1:设置消费的模板(task.hpp)

#pragma once

#include <iostream>
#include <functional>

typedef std::function<int(int, int)> func_t;//c++11特性打包器

class Task
{

public:
    Task(){}
    Task(int x, int y, func_t func):x_(x), y_(y), func_(func)
    {}
    int operator ()() //仿函数
    {
        return func_(x_, y_);
    }
public:
    int x_;
    int y_;
    func_t func_;
};

 部分2:采用raii的方式设置锁的处置方式(lockGuard.hpp)

#pragma once

#include <iostream>
#include <pthread.h>

class Mutex 
{
public:
    Mutex(pthread_mutex_t *mtx):pmtx_(mtx)
    {}
    void lock() 
    {
        std::cout << "要进行加锁" << std::endl;
        pthread_mutex_lock(pmtx_);
    }
    void unlock()
    {
        std::cout << "要进行解锁" << std::endl;
        pthread_mutex_unlock(pmtx_);
    }
    ~Mutex()
    {}
private:
    pthread_mutex_t *pmtx_;
};

// RAII风格的加锁方式
class lockGuard
{
public:
    //构造时加锁
    //析构时解锁
    lockGuard(pthread_mutex_t *mtx):mtx_(mtx)
    {
        mtx_.lock();
    }
    ~lockGuard()
    {
        mtx_.unlock();
    }
private:
    Mutex mtx_; //锁
};

这里解释一下为什么要加锁,因为要维护前提三,也就是要维护互斥的关系。

部分3:阻塞队列这个数据结构

#pragma once

#include <iostream>
#include <queue>
#include <mutex>
#include <pthread.h>
#include "lockGuard.hpp"

const int gDefaultCap = 5;

template <class T>
class BlockQueue
{
private:
    //判断队列的空与满
    bool isQueueEmpty()
    {
        return bq_.size() == 0;
    }
    bool isQueueFull()
    {
        return bq_.size() == capacity_;
    }

public:
    BlockQueue(int capacity = gDefaultCap) : capacity_(capacity)
    {
        pthread_mutex_init(&mtx_, nullptr);
        pthread_cond_init(&Empty_, nullptr);
        pthread_cond_init(&Full_, nullptr);
    }
    void push(const T &in) // 生产者
    {
        
        lockGuard lockgrard(&mtx_); // 自动调用构造函数
        
        //不停的检查直到队列非满
        while (isQueueFull())
            pthread_cond_wait(&Full_, &mtx_); //等待时会自动解锁,消费者可以消费
  
        bq_.push(in); //非满时可以插入任务(数据)
        pthread_cond_signal(&Empty_); //既然插入的数据,那么队列为空这个条件变量就可以解除了。
    } // 自动调用lockgrard 析构函数
    void pop(T *out)
    {
        lockGuard lockguard(&mtx_);
    
        while (isQueueEmpty())
            pthread_cond_wait(&Empty_, &mtx_);
        *out = bq_.front();
        bq_.pop();

        pthread_cond_signal(&Full_);

    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mtx_);
        pthread_cond_destroy(&Empty_);
        pthread_cond_destroy(&Full_);
    }

private:
    std::queue<T> bq_;     // 阻塞队列
    int capacity_;         // 容量上限
    pthread_mutex_t mtx_;  // 通过互斥锁保证队列安全
    pthread_cond_t Empty_; // 用它来表示bq 是否空的条件
    pthread_cond_t Full_;  //  用它来表示bq 是否满的条件
};

部分4:主程序部分,创建线程。

#include "BlockQueue.hpp"
#include "Task.hpp"

#include <pthread.h>
#include <unistd.h>
#include <ctime>

int myAdd(int x, int y) //设置了消费方式
{
    return x + y;
}

void* consumer(void *args) //消费者
{
    BlockQueue<Task> *bqueue = (BlockQueue<Task> *)args;
    while(true)
    {
        // 获取任务
        Task t;
        bqueue->pop(&t);
        // 完成任务
        std::cout << pthread_self() <<" consumer: "<< t.x_ << "+" << t.y_ << "=" << t() << std::endl;
        // sleep(1);
    }

    return nullptr;
}

void* productor(void *args) //生产者
{
    BlockQueue<Task> *bqueue = (BlockQueue<Task> *)args;
    while(true)
    {
        // 制作任务 -- 不一定是从生产者来的
        int x = rand()%10 + 1;
        usleep(rand()%1000);
        int y = rand()%5 + 1;
       
        Task t(x, y, myAdd);
        // 生产任务
        bqueue->push(t);
        // 输出消息
        std::cout <<pthread_self() <<" productor: "<< t.x_ << "+" << t.y_ << "=?" << std::endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    //生成随机数种子
    srand((uint64_t)time(nullptr) ^ getpid() ^ 0x32457);
    
    BlockQueue<Task> *bqueue = new BlockQueue<Task>();

    pthread_t c[2],p[2];
    //创建线程
    pthread_create(c, nullptr, consumer, bqueue);
    pthread_create(c + 1, nullptr, consumer, bqueue);
    pthread_create(p, nullptr, productor, bqueue);
    pthread_create(p + 1, nullptr, productor, bqueue);

    //销毁线程
    pthread_join(c[0], nullptr);
    pthread_join(c[1], nullptr);
    pthread_join(p[0], nullptr);
    pthread_join(p[1], nullptr);

    delete bqueue;

    return 0;
}

结果:

 没有问题,生产者生产一次消费者就消费一次,并且在这之间存在了加解锁的过程。


4.有关效率

有人可能会疑问“明明还是加锁了,那不是没有提升效率嘛”。

由于生产者与消费之间存在消费场所,就可以做到生产者生产的同时消费者从消费场所拿走数据进行消费。因此提升的不是生产者与消费者之间传递数据的速度,而是提升了生产者生产数据的效率与消费者消费数据的效率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/364858.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式事务框架Seata

分布式事务基础 <<分布式事务基础理论>> <<分布式事务解决方案>> Seata 一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的…

Socket编程 | TCP服务器 之 并发阻塞模型(多进程实现)

TCP服务器IO模型 之 并发阻塞 1. 引言 在 Linux 环境下多进程的应用很多,其中最主要的就是网络/客户服务器。多进程服务器是当客户有请求时,服务器用一个子进程来处理客户请求。父进程继续等待其它客户的请求。这种方法的优点是当客户有请求时,服务器能及时处理客户,特别是…

JavaEE简单示例——动态SQL元素<where>

简单介绍&#xff1a; 在我们之前使用where关键字进行查询的时候&#xff0c;总是会在后面添加一个11恒等式&#xff0c;并且在每一个可能拼接的SQL语句前面都加上一个and关键字&#xff0c;防止当后续的所有条件都不满足的时候&#xff0c;where关键字在最后直接跟and的时候也…

系列三、约束

一、概述 1.1、定义 约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。1.2、作用 保证数据库中数据的正确、有效性和完整性。1.3、分类 注意事项&#xff1a;约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的时候添加约束。 二、演示 需求&am…

DEV C++的使用入门程序做算术运算

DEV C Dev-C &#xff08;有时候也称为 Dev-Cpp&#xff09;是一个免费软件&#xff0c;最早是由 BloodShed 公司开发的&#xff0c;在版本 4.9.2 之后该公司停止开发并开放源代码。然后由 Orwell 接手进行维护&#xff0c;陆续开发了几个版本&#xff0c;后来也有其他开发人员…

基于RK3399+Linux QT地面测试台多参数记录仪测试平台软件设计(二)

rk3399 是由本土芯片厂商瑞芯微&#xff08;Rockchip&#xff09;研发的高性能、低功耗“中国芯”。在 2016 年 4 月&#xff0c;rk3399 首次在香港举行的电子展上亮相。芯片使用六核大 LITTLE 处理器&#xff1a; 包括四核的 Cortex-A53 和双核的 Cortex-A72&#xff0c;主频可…

ubuntu22下phpstorm + xdebug调试php

文章目录前言环境主要步骤xedbug的安装xedbug.so的安装浏览器安装xdebug-helperphpstorm 配置xdebug前言 主要借鉴 https://blog.csdn.net/yinhangbbbbb/article/details/79247331 但是在linux下搭建还是遇到了不少问题 环境 phpstudy phpstorm xdebug 主要步骤 phpstudy、…

Nginx学习 (1) —— 初识Nginx(编译安装、启动停止、简单配置)

文章目录Nginx的发行版本Nginx的编译安装&#xff08;ubuntu18.04&#xff09;Nginx相关操作Nginx启动停止命令安装Nginx为系统服务Nginx的目录结构与基本原理目录结构&#xff1a;基本运行原理&#xff1a;基础配置&#xff1a;核心配置Nginx的发行版本 Nginx开源版&#xff…

【UE4 制作自己的载具】1-使用3dsmax制作载具

学习谌嘉诚课程所做笔记源视频链接&#xff1a;【虚幻4】UE4雪佛兰科迈罗汽车详细制作中文教程&#xff01;&#xff08;汽车骨骼绑定驾驶、动画蓝图&#xff09;汽车模型下载链接&#xff1a;https://pan.baidu.com/s/1ZH5gaAwckzRIZ0w6n0qvIA密码&#xff1a;19sj步骤&#x…

【2023蓝桥杯】2017年第八届C/C++A组真题(解析笔记)

目录 T1&#xff1a;迷宫 - 暴力dfs标注 T2&#xff1a;跳蚱蜢 - 9数算式 全排列 枚举乘号位置 T3&#xff1a;魔方状态 - 模拟 判重 &#xff08;高手入*****&#xff09; T4&#xff1a;方格分割 - dfs 从中心点去切割 T5&#xff1a;字母组串 - 递归思维-搞清楚参数…

Spring架构篇--2.5.4 远程通信基础--Select 源码篇--selector.close()总结

前言&#xff1a;通过selector 的poll 来完成所有socket 事件的监听&#xff0c;当不需要selector时 通过selector.close() 完成通道的关闭和资源的释放&#xff1b; 1 selector.close()关闭&#xff1a; AbstractSelector 类中close 方法&#xff1a; public final void cl…

简单说说OSI网络七层模型

如果你读过计算机专业&#xff0c;或者学习过网络通信&#xff0c;那你一定听说过 OSI 模型&#xff0c;它曾无数次让你头大。OSI 是 Open System Interconnection 的缩写&#xff0c;译为“开放式系统互联”。OSI 模型把网络通信的工作分为 7 层&#xff0c;从下到上分别是物理…

你知道这几种常见的JVM调优场景吗?

看此文前需已了解了运行时的数据区域和常用的垃圾回收算法&#xff0c;也了解了Hotspot支持的垃圾回收器。 一、cpu占用过高 cpu占用过高要分情况讨论&#xff0c;是不是业务上在搞活动&#xff0c;突然有大批的流量进来&#xff0c;而且活动结束后cpu占用率就下降了&#xf…

WebGL和OpenGL的区别及关系

什么是WebGLWebGL™是一个跨平台的&#xff0c;免版税的开放Web标准&#xff0c;用于基于OpenGL ES的低级3D图形API&#xff0c;通过HTML5 Canvas元素向ECMAScript公开。熟悉OpenGL ES 2.0的开发人员将使用GLSL将WebGL识别为基于Shader的API&#xff0c;其构造在语义上与底层Op…

FISCO BCOS(二十七)———java操作WeBase

一、搭建fiscobcos环境 1.1、安装jdk1.8 https://blog.csdn.net/weixin_46457946/article/details/1232435131.2、安装mysql https://blog.csdn.net/weixin_46457946/article/details/1232447361.3、安装python https://blog.csdn.net/weixin_46457946/article/details/123…

Lookup argument总览

1. 引言 详情参看Ingonyama团队Tomer 2023年论文《A Brief History of Lookup Arguments》。 Bootle等人2018年论文《Nearly linear-time zero-knowledge proofs for correct program execution》中首次提出了lookup协议&#xff0c;用于证明如下statement&#xff1a; 此处…

Python socket之TCP通信、下载文件

TCP简介TCP介绍TCP协议&#xff0c;传输控制协议&#xff08;英语&#xff1a;Transmission Control Protocol&#xff0c;缩写为 TCP&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议&#xff0c;由IETF的RFC 793定义。TCP通信需要经过创建连接、数据传送、…

手撕八大排序(上)

排序的概念及其引用&#xff1a; 排序的概念&#xff1a; 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有…

Linux上基于PID找到对应的进程名以及所在目录

Linux上基于PID找到对应的进程名以及所在目录前言找到进程的pid通过top命令查看通过 ps -ef |grep nignx进行查看通过端口号进行查看查看nginx进程目录前言 在一台新接触的服务器&#xff0c;却不熟悉搭建所在目录的时候&#xff0c;这时候就就可以通过ps查找进程&#xff0c;并…

巧用性格上的差异来组建团队

你好&#xff0c;我是得物 App 交易平台及中间件平台的 Team Leader Alan。 组建团队过程中&#xff0c;你有没有遇到过类似的场景&#xff1a;团队中某些人之间总是互相不对付、气场不合&#xff0c;不管是日常沟通中还是方案对齐&#xff0c;总是会出现面红耳赤的场面。 从…