C++ 实现生产者消费者模型 (线程同步、互斥锁、条件变量锁)详细注释

news2024/11/14 2:57:18

代码结构

  • 任务:这里用一个int类型的taskNumber代替任务
  • 任务队列类:封装了任务队列,存,取等操作。
  • 生产者工作函数:生产者执行的函数,向任务队列中添加任务,每个生产者生产3个任务
  • 消费者工作函数:消费者执行的函数,从任务队列中拿任务,如果5秒内一直没有任务,则销毁

C++实现代码

#include <stdio.h>
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <unistd.h>
#include <chrono>
using namespace std;

int taskNumber = 0; //任务

class task_queue{
public:
    // 构造函数
    task_queue(int maxNum=10){
        this->maxNum = maxNum;
    }
    // 添加任务
    void add_task(int i){  //i为生产者编号
        myMutex.lock(); // 访问临界资源,加锁
        // 这里必须使用while而不是if,考虑以下情况:
        // A B两个线程同时因为任务队列满而阻塞,现在来了一个空位置,AB同时解除阻塞(因为我们用的是notify_all()函数唤醒所有线程)
        // 这时假设A抢到了互斥锁myMutex并添加任务,之后释放myMutex。这时B又抢到锁,但是还没有空位置,如果用if就会出错。所以需要while循环判断是否有任务
        while(q.size() == maxNum){ //如果任务队列已经满了
            condFull.wait(myMutex); //条件锁阻塞
        }
        q.push(++taskNumber); //添加任务
        condEmpty.notify_all(); //告知因为没有任务而被阻塞的消费者线程解除阻塞
        cout<<"【任务"<<taskNumber<<"】已经被【生产者"<<i<<"】添加到任务队列中"<<endl;
        myMutex.unlock(); //解锁
        
        sleep(1);
        
    }
    // 弹出任务
    bool get_task(int i){ //i为消费者编号
        myMutex.lock(); // 访问临界资源,加锁
        // 使用while而不是if的原因同上,如果条件锁的唤醒函数使用的是notify_one()函数,理论上可以使用if
        while(q.empty()){ //任务队列为空,则等待
            cv_status flag =condEmpty.wait_for( myMutex, chrono::seconds(5)); //等待5秒
            if(flag == std::__1::cv_status::timeout){ //timeout表示5秒都没有任务
                cout<<"【子线程"<<i<<"】退出"<<endl;
                myMutex.unlock(); //这里要解锁,不然退出的线程会一直占用互斥锁导致死锁
                return false; // 等待5秒都没有任务要执行,则退出线程
            }
        }
        int x = q.front(); //取任务
        q.pop();
        condEmpty.notify_all(); //告知因为任务队列满而阻塞的生产者解除阻塞
        cout<<"【消费者"<<i<<"】正在执行【任务"<<x<<"】......"<<endl;
        myMutex.unlock(); //互斥锁解锁
        sleep(1);
        return true;
        
    }
    // 获得当前任务数目
    int get_task_num(){
        lock_guard<mutex>my_lock_guard(myMutex); //使用lockguard自动释放锁
        return (int)q.size();
    }
    
    // 获得最大任务数据
    int get_task_max_num(){
        lock_guard<mutex>my_lock_guard(myMutex); //使用lockguard自动释放锁
        return maxNum;
    }
    
private:
    queue<int>q; //假设一个int代表一个任务task
    int maxNum; //最大任务数
    mutex myMutex; // 临界资源互斥锁
    condition_variable_any condFull; // 任务队列满条件锁
    condition_variable_any condEmpty; // 任务队列空条件锁
};



void producer_task(task_queue &q, int i){ // 生产者工作函数,每个生产者生产3个任务, i(0~4)代表生产者编号
    int num=3;
    while(num--){
        q.add_task(i);
//        sleep(1);
    }
}
void consumer_task(task_queue &q, int i){ // 消费者工作函数,消费者循环消费任务,如果5秒内没有任务则停止工作
    while(1){
        bool flag = q.get_task(i);
//        sleep(1);
        if(flag==false)break;
    }
}

int main(){
    task_queue q(5); //最大任务数为5
    thread producer[5]; // 5个生产者对象
    thread consumer[5]; // 5个消费者对象
    for(int i=0;i<5;++i){
//        producer[i] = thread(&task_queue::add_task, &q, i,i); //给生产者指定任务
//        consumer[i] = thread(&task_queue::get_task, &q,i); //给消费者指定任务
        producer[i] = thread(producer_task, ref(q),i); //给生产者指定任务
        consumer[i] = thread(consumer_task, ref(q),i); //给消费者指定任务
        
    }
    
    for(int i=0;i<5;++i){ //主线程等待子线程执行完毕
        producer[i].join();
        consumer[i].join();
    }
    
    return 0;
}

运行结果:

尾部的运行结果,可以看出任务是按照添加的顺序执行的,在等待5秒之后,线程依次退出
在这里插入图片描述

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

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

相关文章

脱离产品怎么可能完成测试?

“脱离应用场景谈技术毫无意义”。其实很多东西都是如此&#xff0c;这个有点哲理的味道了。我们是做engineering&#xff0c;软件工程也是工程&#xff0c;工程的特点就是不能停留在理论和方法&#xff0c;最后要做出东西来&#xff0c;软的也好&#xff0c;硬的也好。 人有…

爬虫反反爬

目录 为什么要反爬&#xff1f; 经常被反爬的主要人群 常见的反爬策略 通过headers字段来反爬 通过headers中的User-Agent字段来反爬 通过referer字段或者是其他字段来反爬 通过cookie来反爬 通过请求参数来反爬 通过从html静态文件中获取请求数据(github登录数据) 通…

【Go】vscode 安装go环境gopls失败

项目场景&#xff1a; 想要在VSCode安装go环境&#xff0c;但是gopls下载失败&#xff0c;导致vscode无法使用language server 问题描述 自动下载失败&#xff0c;在打开命令面板&#xff08;CtrlshiftP&#xff09;之后&#xff0c;输入go install/update 下载也失败 $ g…

并发编程 - Event Bus 设计模式

文章目录 Pre设计CodeBus接口自定义注解 Subscribe同步EventBus异步EventBusSubscriber注册表RegistryEvent广播Dispatcher 测试简单的Subscriber同步Event Bus异步Event Bus 小结 Pre 我们在日常的工作中&#xff0c;都会使用到MQ这种组件&#xff0c; 某subscriber在消息中间…

PillarNext论文解读

这篇文章是轻舟智航23年的一篇论文&#xff0c;是对pillarNet进行改进。 改进方面&#xff1a; 1.训练更长的时间在检测头增加IOU预测score&#xff0c;这个iou分数预测不太清楚&#xff0c;不知道是不是iouloss 2.扩大感受野&#xff0c;包括Neck部分使用FPN或者BiFPN.使用…

3.zabbix操作二

文章目录 zabbix操作二部署zabbix代理服务器安装zabbix_proxy安装数据库配置代理服务器配置文件web端添加agent代理并连接主机 部署zabbix高可用群集zabbix监控Windows系统zabbix监控java应用zabbix监控SNMP zabbix操作二 部署zabbix代理服务器 分布式监控的作用&#xff1a;…

Flink web UI配置账号密码,权限控制

由于Flink自带的web UI界面没有账号密码&#xff0c;需要通过nginx实现该效果。 1.安装httpd-tools工具 yum install httpd-tools -y 2.生成用户名密码文件 htpasswd -c /usr/local/nginx/conf/flinkuser username passwd flinkuser&#xff1a;为生成的用户名密码文件名称 …

Apache Doris (二十一) :Doris Rollup物化索引创建与操作

目录 1. 创建测试表 2. 创建Rollup物化索引表 3. 查看Rollup物化索引表 4. 删除Rollup物化索引表 5. 验证Rollup物化索引使用 进入正文之前&#xff0c;欢迎订阅专题、对博文点赞、评论、收藏&#xff0c;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; 宝子们点…

open3d 通过vscode+ssh连接远程服务器将可视化界面本地显示

当使用远程服务器时&#xff0c;我们希望能像在本地一样写完代码后能立刻出现一些gui窗口。但是目前网络上的资料都不能很好的解决这个问题。本文尝试尽可能简短地解决这个问题。 步骤 1、在服务器上安装open3d 已经非常简化了&#xff0c;可以使用一行代码完成 pip3 insta…

【Java从入门到大牛】方法详解

&#x1f525; 本文由 程序喵正在路上 原创&#xff0c;CSDN首发&#xff01; &#x1f496; 系列专栏&#xff1a;Java从入门到大牛 &#x1f320; 首发时间&#xff1a;2023年7月9日 &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e…

【计算机组成与体系结构Ⅰ】实验7 IP核的使用、D触发器

一、实验目的 1&#xff1a;学会设计用IP核和原理图的方式设计电路&#xff0c;完成涉及1位数据的2选1多路选择器。 2&#xff1a;设计带异步置零和写使能端的D触发器。 二、实验环境 软件&#xff1a;Vivado 2015.4操作系统&#xff1a;Windows 10 三、实验内容 2.2.1 多路…

49天精通Java,第38天,类加载器,双亲委派机制

目录 一、类加载器子系统的作用1、加载2、链接3、初始化 二、验证【虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁】的代码实例三、类加载器的分类1、启动类加载器&#xff08;引导类加载器&#xff09;2、扩展类加载器3、应用程序类加载器&#xff08;系统…

字典dict的get和setdefault,以及collections的defaultdict

一&#xff1a;dict的get和setdefault 首先&#xff0c;字典dict的get和setdefault的用法都是xxx(key, value)&#xff0c; 都是在字典中查找指定的键并返回值&#xff0c;当查找的key键在字典中存在时&#xff0c;两者作用相同。 参考&#xff1a;https://blog.csdn.net/any1…

「深度学习之优化算法」(十)烟花算法

1. 烟花算法简介 (以下描述,均不是学术用语,仅供大家快乐的阅读)   烟花算法(Firework Algorithm,FWA)是一种受烟花爆炸产生火星,并继续分裂爆炸这一过程启发而得出的算法。算法的思想简单,但具体实现复杂。算法提出时间并不长,但是已经有了不少的改进研究和较为全…

【动手学习深度学习--逐行代码解析合集】11实战Kaggle比赛:预测房价

【动手学习深度学习】逐行代码解析合集 11实战Kaggle比赛&#xff1a;预测房价 视频链接&#xff1a;动手学习深度学习–实战Kaggle比赛&#xff1a;预测房价 课程主页&#xff1a;https://courses.d2l.ai/zh-v2/ 教材&#xff1a;https://zh-v2.d2l.ai/ 1、下载和缓存数据集 …

【JavaEE初阶】JavaScript(WebAPI)

文章目录 1.WebAPI背景知识1.1什么是WebAPI1.2什么是API 2.DOM基本概念2.1什么是DOM2.2常用的DOMAPI2.2.1.选中页面元素2.2.2操作元素的属性1. 事件概念2.获取/修改元素内容3. 获取/修改元素属性4.获取/修改表单元素属性5.获取修改样式属性 2.2.3.操作页面节点1.新增节点2.删除…

关联数组不是线性表

百度百科给的关联数组的解释是&#xff1a; “关联数组”是一种具有特殊索引方式的数组。不仅可以通过整数来索引它&#xff0c;还可以使用字符串或者其他类型的值&#xff08;除了NULL&#xff09;来索引它。 关联数组类似于哈希表&#xff0c;有键-索引&#xff0c;它包含标量…

nunittest如何生成测试报告?我来告诉你

目录 HTMLTestRunner 小试牛刀 1、在unittest中编写测试用例 2、添加报告路径已经报告内容 3、批量执行用例&#xff0c;导入测试报告内容中 4、当然是赶快执行查看报告内容啊 4、添加用例注释&#xff0c;增加报告完整性 总结&#xff1a; 我们做测试的人员们都知道测…

【mysql】—— 数据库基础

序言&#xff1a; 在上期&#xff0c;我们已经安装好了【mysql】。在本期&#xff0c;我将给大家介绍关于数据库的基本知识。 目录 &#xff08;一&#xff09;登陆选项 &#xff08;二&#xff09;基本介绍 1、什么是数据库 2、主流数据库 3、见一见数据库 4、服务器管…