【Linux系统】线程的同步 生产消费模型

同步

同步概念

同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步
竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。在线程场景下,这种问题也不难理解

 如何理解同步?

假如有一个vip自习室,一天小李拿到钥匙进去了,这个自习室只允许进去一个人,外面还排着很多人要进去,但小李拿到了钥匙,一直自习到中午,小李觉得饿了,想去餐厅吃点饭,但小李正想着出去吃饭,又想到这个自习室是免费的,这便宜不赚白不赚,小李刚走出门,又拿着钥匙回来了,可小李又饿了,又出门,又回来了,这样辗转反复造成了小李自身效率低下,排队的其他人造成饥饿问题。

这样小李并没什么错啊,但是不合理,因为不高效!

所以自习室负责人就想到办法了,就规定每一个自习完成的同学归还钥匙后,1.不能立马申请,2.要二次申请,必须排队(换句话说,其他人也必须排队)

这样就可以保证,所有人访问自习室的过程未来是安全的&&具有一定的顺序性,这就是同步,可以是严格的顺序性,也可以是宏观上的相对的顺序性。

 条件变量

 条件变量与互斥锁的接口类似,可以选择全局的或者是局部的。

无非多了一个wait接口!还有broadcast(唤醒全部线程)和signal(唤醒一个线程)接口

 认识条件变量,我们先讲个小故事吧!

现在有两个人,A和B,一个盘子,一个苹果,一个铃铛,B要把苹果放到盘子里,A去拿盘子里的苹果,盘子就是共享资源,那么就要需要加锁和解锁,A去拿苹果时要判断苹果是否存在,如果存在就进去拿苹果,如果不存在,A就需要到铃铛那里等待,当B把苹果成功放入盘子,B会去敲响铃铛,A就会被唤醒重新判断进去拿苹果,往后再有C,D等等来拿苹果时,都需要判断苹果是否存在,不存在就进入铃铛队列中,等待被唤醒!!!

那么此时的铃铛就是条件变量:1.需要一个线程队列。2.需要有通知机制。

而B可以选择叫醒一个,或者叫醒全部!!!这就是条件变量的理解!

 代码测试:

#include<iostream>
#include<unistd.h>
#include<pthread.h>const int num = 5;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t gcond = PTHREAD_COND_INITIALIZER;
void* Wait(void* args)
{std::string name = static_cast<const char*>(args);while(true){pthread_mutex_lock(&mutex);pthread_cond_wait(&gcond, &mutex);usleep(1000);std::cout << "I am:" << name << std::endl;pthread_mutex_unlock(&mutex);//usleep(1000);}
}
int main()
{pthread_t threads[num];for(int i = 0; i < num; i++){char* name = new char[1024];snprintf(name, 1024, "thread-%d", i + 1);pthread_create(threads + i, nullptr, Wait, (void*)name);usleep(1000);}sleep(1);//主线程唤醒其他线程while(true){//pthread_cond_signal(&gcond);pthread_cond_broadcast(&gcond);std::cout << "唤醒一个线程" << std::endl;sleep(1);}for(int i = 0; i < num; i++){pthread_join(threads[i], nullptr);}return 0;
}

 

 

 生产消费模型

理论

 假设有一个超市生产方便面,有对应多个供应商提供商品,多个消费者进行消费,超市不就是一个巨大的缓存吗!让供应商并发的生成商品,消费者又并发的进行消费。这种模型优点,协调忙闲不均,效率高,解耦,都依赖并发的执行!

所以生产消费模型是多执行流并发的模型!

我们要实现生产消费模型,如何思考切入呢?

“321”原则:

  1. 一个交易场所(特定数据结构形式存在的一段内存空间)
  2. 两种角色(生产角色,消费角色)生产线程,消费线程
  3. 三种关系(生产与生产(互斥),消费与消费(互斥),生产与消费(互斥&&同步))

所以实现生产消费模型本质就是通过代码实现“321”原则,用锁和条件变量(或其他方式)来实现三种关系!!!

基于BlockingQueue的生产者消费者模型实现


BlockingQueue本质就是一个队列,常用来实现生产者消费者模型。在队列为空时获取元素的消费者线程就会被阻塞,直到队列中被生产者放入数据并唤醒阻塞的消费者线程。同样的,如果队列为满,那么此时不能再生产数据,生产者线程就会阻塞,直到消费者从队列中拿走数据并唤醒等待的生产者线程!

BlockQueue.hpp --- 主要是阻塞队列的具体实现 

#pragma once
#include<iostream>
#include<string>
#include<queue>const static int defaultcap = 5;
template<class T>
class BlockQueue
{
public:bool IsFull(){return _block_queue.size() == _max_cap;}bool IsEmpty(){return _block_queue.empty();}
public:BlockQueue(int cap = defaultcap):_max_cap(cap){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_c_cond, nullptr);pthread_cond_init(&_p_cond, nullptr);}//假如:两个消费者void Pop(T* out){pthread_mutex_lock(&_mutex);while(IsEmpty()) //if? --》while可以保证代码的鲁棒性(健壮性){//添加尚未满足,但是线程被异常唤醒的情况,叫做伪唤醒!pthread_cond_wait(&_c_cond, &_mutex);//两个消费者都在这里等待,//当两个消费者都被唤醒,其中一个消费者函数返回会往下执行,另一个消费者则会在wait里面的锁内部等待}//1.没有空 || 2. 被唤醒了*out = _block_queue.front();//到阻塞队列消费_block_queue.pop();pthread_mutex_unlock(&_mutex);//唤醒生产者pthread_cond_signal(&_p_cond);}//一个生产者void Equeue(const T& in){pthread_mutex_lock(&_mutex);while(IsFull())  //if?{//满了,生产者不能生产,必须等待//可是在临界区里面啊!//pthread_cond_wait被调用的时候:除了让自己继续排队等待,还会自己释放传入的锁//函数返回时,不就还在临界区了!//返回时:必须先参与锁的竞争,重新加上锁,该函数才会返回pthread_cond_wait(&_p_cond, &_mutex);}//1.没有满 || 2. 被唤醒了_block_queue.push(in);//生产到阻塞队列pthread_mutex_unlock(&_mutex);//唤醒消费者pthread_cond_signal(&_c_cond);//pthread_cond_broadcast:一种场景}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_c_cond);pthread_cond_destroy(&_p_cond);}
private:std::queue<T> _block_queue;//临界资源int _max_cap;pthread_mutex_t _mutex;//消费者的锁pthread_cond_t _c_cond;//消费者的条件变量pthread_cond_t _p_cond;//生产者的条件变量
};

Task.hpp --- 模拟未来要完成的一些任务

#pragma once
#include<iostream>
#include<string>
#include<functional>//typedef std::function<void()> task_t;
using task_t = std::function<void()>;void Download()
{std::cout << "我是一个下载任务" << std::endl;
}
class Task
{
public:Task(){}Task(int x, int y):_x(x), _y(y){}void Excute(){_result = _x + _y;}std::string debug(){std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=?";return msg;}void operator()(){Excute();}std::string result(){std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=" + std::to_string(_result);return msg;}
private:int _x;int _y;int _result;
};

main.cc --- 测试代码逻辑

 

#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include"BlockQueue.hpp"
#include"Task.hpp"void* Consumer(void* args)
{BlockQueue<task_t> *bq = static_cast<BlockQueue<task_t>*>(args);while(true){//sleep(2);//1.获取数据//int data = 0;//Task t;task_t t;bq->Pop(&t);//2.处理数据//t.Excute();t();//std::cout << "Consumer -> " << t.result() << std::endl;}
}
void* Productor(void* args)
{srand(time(nullptr) ^ getpid());BlockQueue<task_t> *bq = static_cast<BlockQueue<task_t>*>(args);while(true){//1.构建数据//int data = rand() % 10 + 1;// int x = rand()%10 + 1;// usleep(x * 1000);// int y = rand()%10 + 1;//Task t(x, y);//2.生产数据bq->Equeue(Download);//std::cout << "Productor -> " << t.debug() << std::endl;std::cout << "Productor -> Download" << std::endl;sleep(1);}
}
int main()
{BlockQueue<task_t> *bq = new BlockQueue<task_t>();pthread_t c1, c2, c3, p1, p2;pthread_create(&c1, nullptr, Consumer, bq);pthread_create(&c2, nullptr, Consumer, bq);pthread_create(&c3, nullptr, Consumer, bq);pthread_create(&p1, nullptr, Productor, bq);pthread_create(&p2, nullptr, Productor, bq);pthread_join(c1, nullptr);pthread_join(c2, nullptr);pthread_join(c3, nullptr);pthread_join(p1, nullptr);pthread_join(p2, nullptr);return 0;
}

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

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

相关文章

[OpenCV] 数字图像处理 C++ 学习——13Canny边缘检测 附完整代码

文章目录 前言1.理论基础(1)高斯模糊平滑图像(GaussianBlur)(2)计算图像梯度(Sobel/Scharr)(3)非极大值抑制 (Non-maximum Suppression)(4)双阈值检测 (Double Threshold)(5)边缘跟踪&#xff08;通过滞后处理&#xff09; 2.代码实现3.完整代码 前言 Canny 边缘检测(高斯滤波…

django学习入门系列之第十点《django中数据库操作--创建与删除表》

文章目录 django创建与删除表开始创建表创建指令新增表删除表删除列新增列修改报错提示语言总结 往期回顾 django创建与删除表 删除表 创建表 修改表 操作目录 开始创建表 class text_into(models.Model):name models.CharField(max_length32)password models.CharField…

Electron快速上手

什么是Electron 一款应用广泛的跨平台的桌面应用开发框架。Electron的本质是结合了 Chromium 与Node.js。使用HTML、CSS、JS 等Web技术构建桌面应用程序。 .vue,.tsx,.less,.ts也可以使用 Electron 流程模型 主进程是纯node环境&#xff0c;可以访问__dirname,fs模块等&#…

DDD设计方法-3-仓储,封装持久化数据

前情提要&#xff1a;一共包含 如下六篇文章&#xff08;篇幅精简&#xff0c;快速入门&#xff09; 1、初识DDD 2、聚合、实体、值对象 3、仓储&#xff0c;封装持久化数据 4、端口和适配器 5、领域事件 6、领域服务&#xff0c;实现约定 DDD设计方法-3-仓储&#xff0c;封装…

绿联充电宝怎么样?深度测评西圣、绿联、倍思磁吸充电宝!

在如今这个电子设备不离手的时代&#xff0c;充电宝成为了我们生活中不可或缺的伙伴。而磁吸充电宝以其便捷的使用方式和时尚的外观&#xff0c;更是受到了众多消费者的青睐&#xff0c;今天&#xff0c;我们将对西圣、绿联、倍思这三个品牌的磁吸充电宝进行深度测评&#xff0…

Three之材质Material

本文目录 前言一、基础材质1.1 特点及属性1.2 代码1.3 效果 二、标准材质2.1 特点及属性2.2 代码及效果 三、深度材质四、法向材质五、朗伯材质六、Phong式材质七、粒子材质八、 着色器材质九、其他材质 前言 Three.js中的材质&#xff08;Material&#xff09;是独立于物体顶点…

Anthropic Claude Artifacts,克劳德聊天机器人如何简化代码编程

最近有一位8岁的小男孩&#xff0c;没有任何编程经验&#xff0c;却成功创建了一个网页游戏。他利用了Claude AI和Cursor来生成代码&#xff0c;这充分展示了人工智能在简化编程和创作过程中的巨大潜力。前几天还能看见一个8岁的女孩用生成式人工智能Cursor ai工具可以搭建出一…

一款基于Vue的低代码可视化表单设计器工具,6K star的可视化表单设计器工具,轻松搞定表单,支持多端适配(附源码)

前言 随着Web应用的日益复杂化&#xff0c;表单的设计与开发成为了许多项目中不可或缺的一环。然而&#xff0c;在实际cao作过程中&#xff0c;表单设计往往面临着重复工作量大、效率低下等问题。那么&#xff0c;是否有一款工具能够简化这一过程&#xff0c;提高开发者的效率…

【MySQL07】【锁】

文章目录 一、前言二、事务的读写情况1. 写-写情况2. 读-写情况3. 一致性读4. 锁定读2.1 共享锁和独占锁2.2 锁定读的语句 5. 写操作 三、多粒度锁四、表锁和行锁1. 表级锁1.1 表级别的 S锁 和 X锁1.2 表级别的 IS 锁和 IX锁1.3 表级别的 AUTO-INC 锁 2. 行级锁2.1 行级锁的分类…

【流式输出】LangChain流式输出的概念

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;JavaScript小贴士 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继续…

记一次头疼事故:springSecurity无法重定向到登录页/springSecurity整合layui后,会话丢失,点击选项卡无法定位到登录页。

1、问题概述? 1、springboot工程引入了springSecurity权限框架实现用户登录功能,当刷新浏览器地址栏的时候能够自动的重定向到登录页实现登录。 2、但是项目中使用了layui的选项卡,当会话丢失的时候(或者重启工程后直接访问),选项卡无法回到登录页,而是选项卡中的数据表…

yolo数据集钢材表面缺陷v8下载适用yolov5等全版本已标注txt格式

钢材表面缺陷检测数据集介绍 数据集概述 本数据集专为钢材表面缺陷检测任务而设计&#xff0c;包含了大量的钢材表面图像&#xff0c;每张图像均带有详细的缺陷标注信息。数据集旨在帮助研究人员和开发人员训练高精度的目标检测模型&#xff0c;以应用于钢材制造和质量控制等多…

建网站公司策划书

建网站公司策划书&#xff1a;建设创新型网站服务企业 一、背景介绍 随着信息时代的来临&#xff0c;互联网已经成为人们生活和工作的重要组成部分。在这个数字化的时代&#xff0c;企业需要强大的在线存在来吸引客户、展示产品和提供服务。为满足市场需求&#xff0c;我们公…

图形语言传输格式glTF和三维瓦片数据3Dtiles(b3dm、pnts)学习

文章目录 3DTilesb3dm一、glTF1.glTF 3D模型格式有两种2.glTF 场景描述结构3.glTF的JSON结构 二、 3DTiles 原文 工具资料 格式详解 格式详解&#xff01; 3D Tiles 是一种开源的、优化的文件格式&#xff0c;支持逐级细节&#xff08;LOD&#xff09;和空间索引&#xff0c;使…

JMeter 快速入门体验,小白也能看得懂!

最近在推进信创转 arm, 遇到的问题是有几个服务在 x86 架构下运行良好&#xff0c;但是在 arm 机器上总是出现问题&#xff0c;为了验证问题&#xff0c;准备使用压测工具做一下压力测试&#xff0c;JMeter 免费开源又好用&#xff0c;趁此机会学习实践一下吧。 JMeter 是开源…

高德地图-小米14 Pro 定制版 v12.10.61.3021 简洁版

高德地图小米14 Pro 定制版是一款专门为小米14 Pro 设计的简洁版高德地图。相较于普通版本&#xff0c;该版本体积更小&#xff0c;运行速度更快&#xff0c;并且没有广告。支持驾车、骑行、公交地铁、步行等多种导航模式&#xff0c;使用北斗卫星导航系统&#xff0c;精准度非…

【机器人学】7-4.六自由度机器人自干涉检测-两圆柱体空间关系【附MATLAB代码】

目录 前言 公式推导 MATLAB代码 前言 前面介绍了两个圆柱的旋转变换&#xff0c;已将两个圆柱体旋转到了比较好分析的位置&#xff0c;下面将正式分析两个圆柱体的位置关系。会借用投影的思想。 一 根据机械臂的几何数据以及DH参数&#xff0c;确定机械臂等…

硬件产品经理进阶:产品层次划分的3个方法

目录 1、内容简介 2、产品三层次概念 3、产品四层次概念 4、产品五层次概念 作者简介 1、内容简介 产品本身指的是能够满足需求和欲望的一种媒介物。 可以是实体、也可以是虚拟的服务。 在产品竞争白热化的今天&#xff0c; 如果只是考虑把产品做出来、 仅仅在实际产…

保存json时,保存成自己喜欢的格式的方法(而不是直接保存成格式化的json文档)

保存json时&#xff0c;不是直接保存成格式化的json文档的格式的方法 前言&#xff0c;博主是如何把格式话的json格式保存成自己喜欢的json格式的保存成格式化的json文档的格式&#xff1a;带缩进格式全部保存成一行每条数据保存成一行&#xff1a; 保存成自己喜欢的格式碎碎念…

红黑树的插入 C++

红黑树与二叉搜索树类似 它在每个节点增加了一个存储位记录节点的颜色&#xff0c;可以是RED,也可以是BLACK&#xff1b;通过任意一条从根到叶子简单路径上颜色的约束&#xff0c;红黑树保证最长路径不超过最短路径的二倍&#xff0c;因而近似平衡&#xff08;最短路径就是全黑…