Golang 并发 生产者消费者模式
Golang 并发 生产者消费者模式
生产者-消费者模式能够带来的好处
生产者消费者模式是一种常见的并发编程模式,用于解决生产者和消费者之间的数据传递和处理问题。在该模式中,生产者负责生成数据(生产),而消费者负责处理数据(消费)。生产者和消费者在时间上是解耦的,它们可以独立地以不同的速度执行。生产者消费者模式在并发编程中具有重要性,有以下几个方面的作用:
- 解耦生产者和消费者: 生产者和消费者之间通过中间的数据缓冲区(如通道)进行通信,从而实现了解耦。生产者和消费者可以独立地进行工作,无需关心对方的状态或执行速度。
- 平衡资源利用和处理能力: 生产者消费者模式可以平衡生产者和消费者之间的资源利用和处理能力。生产者可以根据消费者的处理能力进行生产,并且消费者可以根据生产者的速度进行消费,从而避免资源的浪费或瓶颈。
- 提高系统的并发性和响应性: 生产者消费者模式允许并发执行生产者和消费者的任务,从而提高系统的并发性和响应性。通过并发处理数据,可以更好地利用多核处理器和异步执行,从而加快系统的处理速度。
- 实现异步通信和处理: 生产者消费者模式使得生产者和消费者可以异步地进行数据通信和处理。生产者可以在需要时生成数据,并将其放入缓冲区中,而消费者可以在需要时从缓冲区中获取数据进行处理,从而实现异步的数据交换和处理。
- 提供可扩展性和模块化: 生产者消费者模式提供了一种可扩展和模块化的设计方式。通过将生产者和消费者解耦,可以方便地添加更多的生产者或消费者,以适应系统需求的变化,同时保持代码的可读性和维护性。
总之,生产者消费者模式在并发编程中起着重要的作用,通过解耦、平衡资源利用、提高并发性和响应性等方面的优势,可以帮助构建高效、可扩展的并发系统。
应用场景
- 日志处理: 在日志处理中,可以将日志的生成视为生产者,而日志的消费(如写入文件、发送到远程服务器等)视为消费者。通过使用一个日志通道,生产者可以将日志消息发送到通道,而消费者则从通道中接收日志消息并进行相应的处理。这样可以有效地解耦日志的生成和消费,避免日志处理对业务逻辑的影响。
- 任务队列: 在某些任务调度和处理场景中,可以使用生产者消费者模式来实现任务队列。生产者负责将任务添加到队列中,而消费者则从队列中获取任务并进行处理。这种方式可以实现任务的异步处理和负载均衡,提高系统的并发性能。
- 缓存更新: 在某些缓存系统中,生产者消费者模式可用于实现缓存更新的异步处理。当数据发生变化时,生产者负责生成更新请求,而消费者则负责将更新应用到缓存中。通过将更新请求发送到缓存通道,可以实现异步的缓存更新,提高系统的响应性能和吞吐量。
channel的实现
package chapter03import ("fmt""testing""time"
)func producer(ch chan<- int) {for i := 1; i <= 5; i++ {// 将数据发送到通道ch <- ifmt.Println("生产者生产:", i)// 模拟生产过程time.Sleep(time.Second)}close(ch) // 关闭通道
}
func consumer(ch <-chan int, done chan<- bool) {for num := range ch {fmt.Println("消费者消费:", num)// 模拟消费过程time.Sleep(2 * time.Second)}// 通知主线程消费者已完成done <- true
}
func TestRun(t *testing.T) {// 创建带缓冲的通道ch := make(chan int, 3)// 用于通知主线程消费者已完成done := make(chan bool)// 启动生产者goroutinego producer(ch)// 启动消费者goroutinego consumer(ch, done)// 主线程等待消费者完成<-donefmt.Println("消费者已完成")// 主线程结束,程序退出
}
代码输出如下
=== RUN TestRun
生产者生产: 1
消费者消费: 1
生产者生产: 2
消费者消费: 2
生产者生产: 3
生产者生产: 4
生产者生产: 5
消费者消费: 3
消费者消费: 4
消费者消费: 5
消费者已完成
--- PASS: TestRun (10.00s)
- producer函数是生产者函数,它通过通道将数据发送到消费者。
- consumer函数是消费者函数,它从通道中接收数据并进行消费。
- 主函数是程序的入口,它创建了一个整型通道和一个用于通知消费者完成的通道。
互斥锁和条件变量的实现
package chapter03import ("fmt""sync""testing""time"
)type Data struct {Value int
}
type Queue struct {mutex sync.Mutexcond *sync.Condbuffer []Dataterminated bool
}func NewQueue() *Queue {q := &Queue{}q.cond = sync.NewCond(&q.mutex)return q
}
func (q *Queue) Produce(data Data) {q.mutex.Lock()defer q.mutex.Unlock()q.buffer = append(q.buffer, data)fmt.Printf("Produced: %d\n", data.Value)// 唤醒等待的消费者q.cond.Signal()
}
func (q *Queue) Consume() Data {q.mutex.Lock()defer q.mutex.Unlock()// 等待数据可用for len(q.buffer) == 0 && !q.terminated {q.cond.Wait()}if len(q.buffer) > 0 {data := q.buffer[0]q.buffer = q.buffer[1:]fmt.Printf("Consumed: %d\n", data.Value)return data}return Data{}
}
func (q *Queue) Terminate() {q.mutex.Lock()defer q.mutex.Unlock()q.terminated = true// 唤醒所有等待的消费者q.cond.Broadcast()
}
func TestRun02(t *testing.T) {queue := NewQueue()// 启动生产者for i := 1; i <= 3; i++ {go func(id int) {for j := 1; j <= 5; j++ {data := Data{Value: id*10 + j}queue.Produce(data)time.Sleep(time.Millisecond * 500) // 模拟生产时间}}(i)}// 启动消费者for i := 1; i <= 2; i++ {go func(id int) {for {data := queue.Consume()if data.Value == 0 {break}// 处理消费的数据time.Sleep(time.Millisecond * 1000) // 模拟处理时间}}(i)}// 等待一定时间后终止消费者time.Sleep(time.Second * 6)queue.Terminate()// 等待生产者和消费者完成time.Sleep(time.Second * 1)
}
代码输出如下
=== RUN TestRun02
Produced: 11
Consumed: 11
Produced: 31
Consumed: 31
Produced: 21
Produced: 32
Produced: 22
Produced: 12
Consumed: 21
Consumed: 32
Produced: 23
Produced: 13
Produced: 33
Produced: 24
Produced: 34
Produced: 14
Consumed: 22
Consumed: 12
Produced: 35
Produced: 15
Produced: 25
Consumed: 23
Consumed: 13
Consumed: 33
Consumed: 24
Consumed: 34
Consumed: 14
Consumed: 35
Consumed: 15
--- PASS: TestRun02 (7.00s)
在上述示例中,
我们创建了一个 Queue 结构体,其中包含了一个互斥锁和一个条件变量。生产者通过 Produce 方法向队列中添加数据,并使用条件变量的 Signal 方法唤醒等待的消费者。消费者通过 Consume 方法从队列中取出数据,如果队列为空且未终止,则通过条件变量的 Wait 方法来阻塞自己。当有数据被生产或终止信号发出时,生产者唤醒等待的消费者。
在主函数中,我们启动了多个生产者和消费者的 goroutine,它们并发地进行生产和消费操作。通过适当的延时模拟生产和消费的时间,展示了生产者和消费者之间的协调工作。
最后,我们通过调用 queue.Terminate() 方法来终止消费者的执行,并通过适当的延时等待生产者和消费者完成。
通过使用互斥锁和条件变量,我们可以实现生产者消费者模式的线程安全同步,确保生产者和消费者之间的正确交互。这种实现方式具有较低的复杂性,并提供了对共享资源的有效管理和控制。
参考
- https://www.jb51.net/jiaoben/2850675mz.htm
相关文章:
Golang 并发 生产者消费者模式
Golang 并发 生产者消费者模式 生产者-消费者模式能够带来的好处 生产者消费者模式是一种常见的并发编程模式,用于解决生产者和消费者之间的数据传递和处理问题。在该模式中,生产者负责生成数据(生产),而消费者负责处…...
Win32 SDK Gui编程系列之--ListView自绘OwnerDraw
ListView自绘OwnerDraw 1.ListView自绘OwnerDraw 正在试错是否使用了列表视图,尽量制作出智能的表格编辑器。本页显示了业主抽签的表格数据(二维数组数据)的显示方法。 显示画面和整个程序如下所示。使用ListView_GetSubItemRect宏的话,就不需要getRect函数了。 当nCol的…...
深度学习本科课程 实验5 循环神经网络
循环神经网络实验 任务内容 理解序列数据处理方法,补全面向对象编程中的缺失代码,并使用torch自带数据工具将数据封装为dataloader分别采用手动方式以及调用接口方式实现RNN、LSTM和GRU,并在至少一种数据集上进行实验从训练时间、预测精度、…...
Redis篇之过期淘汰策略
一、数据的过期策略 1.什么是过期策略 Redis对数据设置数据的有效时间,数据过期以后,就需要将数据从内存中删除掉。可以按照不同的规则进行删除,这种删除规则就被称之为数据的删除策略(数据过期策略)。 2.过期策略-惰…...
【Kubernetes】kubectl top pod 异常?
目录 前言一、表象二、解决方法1、导入镜像包2、编辑yaml文件3、解决问题 三、优化改造1.修改配置文件2.检查api-server服务是否正常3.测试验证 总结 前言 各位老铁大家好,好久不见,卑微涛目前从事kubernetes相关容器工作,感兴趣的小伙伴相互…...
前后端分离项目:前端的文件夹应该叫什么名字,后端呢
在前后端分离的项目中,为了提高项目的可读性和易管理性,给前端和后端的文件夹选择合适的名字是很重要的。这里提供一些建议,但请记住,最终的命名应该根据你的团队习惯、项目特性以及可能的公司规定来决定。 ### 前端文件夹命名建…...
2024.2.6
1.现有无序序列数组为23,24,12,5,33,5347,请使用以下排序实现编程 函数1:请使用冒泡排序实现升序排序 函数2:请使用简单选择排序实现升序排序 函数3:请使用快速排序实现升序排序 函数4:请使用插入排序实现升序排序 #include<stdio.h> #include<string.h&g…...
如何在 Microsoft Azure 上部署和管理 Elastic Stack
作者:来自 Elastic Osman Ishaq Elastic 用户可以从 Azure 门户中查找、部署和管理 Elasticsearch。 此集成提供了简化的入门体验,所有这些都使用你已知的 Azure 门户和工具,因此你可以轻松部署 Elastic,而无需注册外部服务或配置…...
在Visual Studio中引用和链接OpenSceneGraph (OSG) 库
在Visual Studio中引用和链接OpenSceneGraph (OSG) 库,按照以下步骤操作: 构建或安装OSG库 下载OpenSceneGraph源代码(如3.0版本)并解压。使用CMake配置项目,为Visual Studio生成解决方案文件。通常您需要设置CMake中的…...
[缓存] - Redis
0.为什么要使用缓存? 用缓存,主要有两个用途:高性能、高并发。 1. 高性能 尽量使用短key 不要存过大的数据 避免使用keys *:使用SCAN,来代替 在存到Redis之前压缩数据 设置 key 有效期 选择回收策略(maxmemory-policy) 减…...
spring boot和spring cloud项目中配置文件application和bootstrap加载顺序
在前面的文章基础上 https://blog.csdn.net/zlpzlpzyd/article/details/136060312 日志配置 logback-spring.xml <?xml version"1.0" encoding"UTF-8"?> <configuration scan"true" scanPeriod"10000000 seconds" debug…...
AdaBoost算法
Boosting是一种集成学习方法,AdaBoost是Boosting算法中的一种具体实现。 Boosting方法的核心思想在于将多个弱分类器组合成一个强分类器。这些弱分类器通常是简单的模型,比如决策树,它们在训练过程中的错误会被后续的弱分类器所修正。Boosti…...
基于 elasticsearch v8 的 CRUD 操作及测试用例
基于 elasticsearch v8 的 CRUD 操作及测试用例 https://github.com/chenshijian73-qq/go-es/tree/main...
深度学习的新进展:解析技术演进与应用前景
深度学习的新进展:解析技术演进与应用前景 深度学习,作为人工智能领域的一颗璀璨明珠,一直以来都在不断刷新我们对技术和未来的认知。随着时间的推移,深度学习不断迎来新的进展,这不仅推动了技术的演进,也…...
【第二届 Runway短视频创作大赛】——截至日期2024年03月01日
短视频创作大赛 关于AI Film Festival竞赛概况参加资格报名期间报名方法 提交要求奖品附录 关于AI Film Festival 2022年成立的AIFF是一个融合了最新AI技术于电影制作中的艺术和艺术家节日,让我们得以一窥新创意时代的风采。从众多参赛作品中…...
UniApp 快速上手与深度学习指南
一、UniApp 简介 UniApp 是中国DCloud公司研发的一款创新的跨平台应用开发框架,它基于广受欢迎的前端开发库Vue.js,旨在解决多端适配和快速开发的问题。通过UniApp,开发者能够采用一套统一的代码结构、语法和API来构建应用程序,从而实现真正意义上的“一次编写,到处运行”…...
10个简单有效的编辑PDF文件工具分享
10个编辑PDF文件工具作为作家、编辑或专业人士,您可能经常发现自己在处理 PDF 文件。无论您是审阅文档、创建报告还是与他人共享工作,拥有一个可靠的 PDF 编辑器供您使用都非常重要。 10个简单适用的编辑PDF文件工具 在本文中,我们将介绍当今…...
电力负荷预测 | 基于GRU门控循环单元的深度学习电力负荷预测,含预测未来(Python)
文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 电力负荷预测 | 基于GRU门控循环单元的深度学习电力负荷预测,含预测未来(Python&...
vue 实现 手机号中间4位分格输入框(暂无选中标识
vue 实现 手机号中间4位分格输入框 效果图 <!--4位分格输入框--> <!--<template><div><div style"display: flex;"><div class"phone-input"><inputv-for"(digit, index) in digits":key"index"…...
#免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
Mac电脑苹果芯片读写NTFS硬盘bash脚本 (ntfs.sh脚本内容在本文最后面) ntfs.sh脚本可以将Mac系统(苹果M系芯片)上的NTFS硬盘改成可读写的挂载方式,从而可以直接往NTFS硬盘写入数据。此脚本免费,使用过程中无需下载任何收费软件。…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
