linux:生产者消费者模型
个人主页 : 个人主页
个人专栏 : 《数据结构》 《C语言》《C++》《Linux》
文章目录
- 前言
- 一、生产者消费者模型
- 二、基于阻塞队列的生产者消费者模型
- 代码实现
- 总结
前言
本文是对于生产者消费者模型的知识总结
一、生产者消费者模型
生产者消费者模型就是通过一个容器来解决生产者消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而是通过之间的容器来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接交给容器,消费者不找生产者要数据,而是直接从容器中取数据,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力,其中这个容器就是用于生产者和消费者之间解耦的
(强耦合是指两个或多个系统,组件或模块之间存在紧密依赖关系。)
特点:
- 三种关系:生产者与生产者之间(互斥),消费者与消费者之间(互斥),生产者与消费者之间(互斥 && 同步)
- 两种角色:生产者和消费者
- 一个交易(通讯)场所:一个容器(一段内存空间)
因为我们是多个线程访问同一个容器,那必然会导致数据不一致的问题,所以我们需要对该临界资源加锁,所以生产者与生产者之间,消费者与消费者之间,生产者与消费者之间都是互斥的。
又因为容器可能为空(满),此时消费者(生产者)还一直在临界区申请锁,又因没有数据(空间)而释放锁,从而不断申请锁释放锁,导致生产者(消费者)的饥饿问题。此时我们就需要生产者与消费者之间的同步。
对于2,3两点,这很好理解不解释。
我们编写生产者消费者模型的本质就是对以上三点的维护。(互斥保证数据安全,同步保证效率)
优点:
- 解耦
- 支持并发
- 支持忙闲不均
对于第一点,不就是生产者与消费者通过容器来解耦提升效率(如果没有这个容器,则生产者生产完数据,就必须等待消费者来接受处理数据,不能立刻继续生产数据)。
对于第二点,当生产者在生产数据时,消费者也同时在处理数据
对于第三点,当生产者生产数据的速度超过消费者的处理能力时,容器可以起到缓存的作用,将多余的数据暂时存储,等待消费者有空闲时再进行处理。如果消费者处理数据的能力超过生产者时,同理。
二、基于阻塞队列的生产者消费者模型
在多线程编程中阻塞队列(Blocking Queue)是一种常用于实现生产者和消费者模型的数据结构。
- 队列为空时,从队列中取数据将被阻塞,直到队列中有数据时被唤醒。
- 队列为满时,向队列中放入数据将被阻塞,直到队列中有数据取出被唤醒。
代码实现
下面是一个单生成单消费模型
LockGuard.hpp 文件 将加锁释放锁,交给一个对象处理,当对象创建加锁,对象销毁释放锁
#pragma once
#include <pthread.h>class Mutex
{
public:Mutex(pthread_mutex_t *mutex):_mutex(mutex){}void Lock(){pthread_mutex_lock(_mutex);}void UnLock(){pthread_mutex_unlock(_mutex);}~Mutex(){}
private:pthread_mutex_t *_mutex;
};class LockGuard
{
public:LockGuard(pthread_mutex_t *mutex): _lock(mutex){_lock.Lock();}~LockGuard(){_lock.UnLock();}
private:Mutex _lock;
};
Blockqueue.hpp 文件
#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include "LockGuard.hpp"using namespace std;
const int CAPACITY = 5;template<class T>
class BlockQueue
{
public:BlockQueue(int cap = CAPACITY):_capacity(cap){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_p, nullptr);pthread_cond_init(&_c, nullptr);}bool isFull(){return _bq.size() == _capacity;}void Push(const T &in){LockGuard mutex(&_mutex);//pthread_mutex_lock(&_mutex);while(isFull()){pthread_cond_wait(&_p, &_mutex);}_bq.push(in);// 唤醒策略为 生产一个,消费一个pthread_cond_signal(&_c);//pthread_mutex_unlock(&_mutex);}bool isEmpty(){return _bq.size() == 0;}void Pop(T *out){LockGuard mutex(&_mutex);//pthread_mutex_lock(&_mutex);while(isEmpty()){pthread_cond_wait(&_c, &_mutex);}*out = _bq.front();_bq.pop();// 唤醒策略为 消费一个,生产一个pthread_cond_signal(&_p);//pthread_mutex_unlock(&_mutex);}~BlockQueue(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_p);pthread_cond_destroy(&_c);}
private:queue<T> _bq;int _capacity;pthread_mutex_t _mutex;pthread_cond_t _p;pthread_cond_t _c;
};
Task.hpp 文件
#pragma once
#include <string>const char *opers = "+-*/%";enum
{ok = 0,div_zero,mod_zero
};class Task
{
public:Task(){}Task(int x, int y, char op) : _data_x(x), _data_y(y), _oper(op){_code = ok;}void Run(){switch (_oper){case '+':_result = _data_x + _data_y;break;case '-':_result = _data_x - _data_y;break;case '*':_result = _data_x * _data_y;break;case '/':{if(_data_y == 0){_code = div_zero;}else{_result = _data_x / _data_y;}}break;case '%':{if(_data_y == 0){_code = mod_zero;}else{_result = _data_x % _data_y;}}break;default:break;}}void operator()(){Run();}std::string PrintTask(){std::string ret = std::to_string(_data_x);ret += _oper;ret += std::to_string(_data_y);ret += "=?";return ret;}std::string PrintResult(){std::string ret = std::to_string(_data_x);ret += _oper;ret += std::to_string(_data_y);ret += "=";if(_code == ok){ret += std::to_string(_result);}else{ret += "?";}ret += "[";ret += std::to_string(_code);ret += "]";return ret;}~Task(){}private:int _data_x;int _data_y;char _oper;int _result;int _code; // 错误码
};
Main.cc 文件
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include <string.h>
#include "BlockQueue.hpp"
#include "Task.hpp"
using namespace std;void *producer(void *args)
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);// 产生任务while (true){int x = rand() % 10 + 1;int y = rand() % 10 + 1;char oper = opers[rand() % strlen(opers)];Task task(x, y, oper);cout << "producer: " << task.PrintTask() << endl;bq->Push(task);sleep(1);}return nullptr;
}void *consumer(void *args)
{// usleep(1000);BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);// 获取任务,处理任务while (true){if (bq->isFull()){Task task;bq->Pop(&task);task();cout << "consumer: " << task.PrintResult() << endl;//sleep(1);}}
}int main()
{srand(time(nullptr) ^ getpid());BlockQueue<Task> bq;pthread_t p;pthread_create(&p, nullptr, producer, (void *)&bq);pthread_t c;pthread_create(&c, nullptr, consumer, (void *)&bq);pthread_join(p, nullptr);pthread_join(c, nullptr);return 0;
}
那如何将这个单生产单消费该为多生产多消费呢?因为多生产多消费本质也是多个线程访问临界资源,那我们单生产和单消费不也是多个线程访问临界资源吗,所以我们不需要对BlockQueue.hpp文件进行修改,只需要在main函数中,创建多个生产者和消费者即可。
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include <string.h>
#include "BlockQueue.hpp"
#include "Task.hpp"
using namespace std;template <class T>
class ThreadData
{
public:ThreadData(pthread_t tid, const string threadname, BlockQueue<T> *bq): _tid(tid), _threadname(threadname), _bq(bq){}public:pthread_t _tid;string _threadname;BlockQueue<T>* _bq;
};void *producer(void *args)
{ThreadData<Task> *data = static_cast<ThreadData<Task> *>(args);// 产生任务while (true){int x = rand() % 10 + 1;int y = rand() % 10 + 1;char oper = opers[rand() % strlen(opers)];Task task(x, y, oper);cout << data->_tid << ", " << data->_threadname <<": " << task.PrintTask() << endl;data->_bq->Push(task);sleep(1);}return nullptr;
}void *consumer(void *args)
{// usleep(1000);ThreadData<Task> *data = static_cast<ThreadData<Task> *>(args);// 获取任务,处理任务while (true){if (data->_bq->isFull()){Task task;data->_bq->Pop(&task);task();cout << data->_tid << ", " << data->_threadname << ": " << task.PrintResult() << endl;// sleep(1);}}
}int main()
{srand(time(nullptr) ^ getpid());BlockQueue<Task> bq;pthread_t p1;ThreadData<Task> data1(p1, "product-1", &bq);pthread_create(&p1, nullptr, producer, (void *)&data1);pthread_t p2;ThreadData<Task> data2(p2, "product-2", &bq);pthread_create(&p2, nullptr, producer, (void *)&data2);pthread_t c1;ThreadData<Task> data3(c1, "consumer-1", &bq);pthread_create(&c1, nullptr, consumer, (void *)&data3);pthread_t c2;ThreadData<Task> data4(c2, "consumer-2", &bq);pthread_create(&c2, nullptr, consumer, (void *)&data4);pthread_join(p1, nullptr);pthread_join(p2, nullptr);pthread_join(c1, nullptr);pthread_join(c2, nullptr);return 0;
}
总结
以上就是我对于线程同步的总结。
相关文章:

linux:生产者消费者模型
个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、生产者消费者模型二、基于阻塞队列的生产者消费者模型代码实现 总结 前言 本文是对于生产者消费者模型的知识总结 一、生产者消费者模型 生产者消费者模型就是…...

C++教学——从入门到精通 5.单精度实数float
众所周知,三角形的面积公式是(底*高)/2 那就来做个三角形面积计算器吧 到吗如下 #include"bits/stdc.h" using namespace std; int main(){int a,b;cin>>a>>b;cout<<(a*b)/2; } 这不对呀,明明是7.5而他却是7,…...
面向对象设计之单一职责原则
设计模式专栏:http://t.csdnimg.cn/6sBRl 目录 1.单一职责原则的定义和解读 2.如何判断类的职责是否单一 3.类的职责是否越细化越好 4.总结 1.单一职责原则的定义和解读 单一职责原则(Single Responsibility Principle,SRP)的描述:一个类…...

蓝桥杯真题:单词分析
import java.util.Scanner; //1:无需package //2: 类名必须Main, 不可修改 public class Main{public static void main(String[]args) {Scanner sannernew Scanner(System.in);String strsanner.nextLine();int []anew int [26];for(int i0;i<str.length();i) {a[str.charA…...

Python字符串字母大小写变换,高级Python开发技术
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ‘’’ demo ‘tHis iS a GOod boOK.’ print(demo.casefold()) print(demo.lower()) print(demo.upper()) print(demo.capitalize()) print(demo.title()) print(dem…...
CentOS常用功能命令集合
1、删除指定目录下所有的空目录 find /xxx -type d -empty -exec rmdir {} 2、删除指定目录下近7天之前的日志文件 find /xxx -name "*.log" -type f -mtime 7 -exec rm -f {} \; 3、查询指定目录下所有的指定格式文件(比如PDF文件) find…...

黑马点评项目笔记 II
基于Stream的消息队列 stream是一种数据类型,可以实现一个功能非常完善的消息队列 key:队列名称 nomkstream:如果队列不存在是否自动创建,默认创建 maxlen/minid:设置消息队列的最大消息数量 *|ID 唯一id:…...
关于一篇知乎答案的重现
〇、前言 早上在逛知乎的时候,瞥见了一篇答案:如何通俗解释Docker是什么?感觉很不错,然后就耐着性子看了下,并重现了作者的整个过程。但是并不顺利,记载一下这些坑。嫌麻烦的话可以直接clone 研究…...
实时数据库测试-汇编小程序
实时数据库测试-汇编小程序。 hd.asm .686 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\kernel32.inc include \masm32\include\user32.inc include \masm32\include\gdi32.inc …...
HTML5 、CSS3 、ES6 新特性
HTML5 新特性 1. 新的语义化元素:article 、footer 、header 、nav 、section 2. 表单增强,新的表单控件:calendar 、date 、time 、email 、url 、search 3. 新的 API:音频(用于媒介回放的 video 和 audio 元素)、图形&#x…...

基于springboot+vue实现的驾校信息管理系统
作者主页:Java码库 主营内容:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】:Java 【框架】:spring…...
X进制减法(贪心算法C++实现)
题目 进制规定了数字在数位上逢几进一。 X 进制是一种很神奇的进制,因为其每一数位的进制并不固定! 例如说某种 X 进制数,最低数位为二进制,第二数位为十进制,第三数位为八进制,则 X 进制数 321 转换为十…...

[Windows]服务注册工具(nssm)
文章目录 官网下载地址百度云下载地址NSSM常用命令 使用场景:例如现在我们想开启自动启动一个Java服务,nginx,node等。 官网下载地址 https://nssm.cc/download 百度云下载地址 链接:https://pan.baidu.com/s/111fkBWIS7CTlWIj80Kc8Sg?pwdanan 提取码…...
Xilinx缓存使用说明和测试
Xilinx缓存使用说明和测试 1 BRAM说明2 FIFO说明3 实例测试3.1 代码3.2 仿真本文主要介绍Xilinx FPGA芯片中BRAM和FIFO的使用方法和测试结果,主要针对流接口进行仿真。 1 BRAM说明 BRAM是Xilinx芯片中重要的存储资源,其可配置为单端口RAM/ROM或者双端口RAM/ROM,本文以最复杂…...
LeetCode:2952. 需要添加的硬币的最小数量(贪心 Java)
目录 2952. 需要添加的硬币的最小数量 题目描述: 实现代码与解析: 贪心 原理思路: 2952. 需要添加的硬币的最小数量 题目描述: 给你一个下标从 0 开始的整数数组 coins,表示可用的硬币的面值,以及一个…...

企业员工在线培训系统功能介绍
随着信息技术的飞速发展,企业员工培训方式正逐步从传统的面授转向灵活高效的在线培训。一个综合性的企业员工在线培训系统能够为员工提供多样化的学习资源、便捷的学习途径和有效的学习监督,以下是该系统的主要功能详细介绍: 1. 课程功能 线…...
服了,一线城市的后端都卷成这样了吗!?
声明:本文首发在同名公众号:王中阳Go,未经授权禁止转载。 先听TA的故事 投稿的主人公是一名工作5年的后端开发工程师,最近2年用Golang,之前其他语言。去年春节前被裁员了,各种心酸史,好愁人啊。…...

Qt扫盲-QAssisant 集成其他qch帮助文档
QAssisant 集成其他qch帮助文档 一、概述二、Cmake qch例子1. 下载 Cmake.qch2. 添加qch1. 直接放置于Qt 帮助的目录下2. 在 QAssisant中添加 一、概述 QAssisant是一个很好的帮助文档,他提供了供我们在外部添加新的 qch帮助文档的功能接口,一般有两中添…...
[lesson01]学习C++的意义
学习C的意义 C语言特点 C语言是在实践的过程中逐步完善起来的 没有深思熟路的设计过程残留量过多低级语言的特征 C语言的目标是高效 最终程序执行效率的高效 软件方法论的发展 面相过程程序设计:数据结构 算法 主要解决科学计算问题,用户需求简单而…...

LabVIEW双通道太阳射电频谱观测系统
LabVIEW双通道太阳射电频谱观测系统 开发了一个基于LabVIEW平台开发的双通道高速太阳射电频谱观测系统。该系统实时监测太阳射电爆发,具有随机性、持续时间短、变化快等特点。通过高速信号采集卡实现1.5 GS/s的信号采集,时间分辨率可达4ms,频…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...