当前位置: 首页 > news >正文

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 并发 生产者消费者模式 生产者-消费者模式能够带来的好处 生产者消费者模式是一种常见的并发编程模式&#xff0c;用于解决生产者和消费者之间的数据传递和处理问题。在该模式中&#xff0c;生产者负责生成数据&#xff08;生产&#xff09;&#xff0c;而消费者负责处…...

Win32 SDK Gui编程系列之--ListView自绘OwnerDraw

ListView自绘OwnerDraw 1.ListView自绘OwnerDraw 正在试错是否使用了列表视图,尽量制作出智能的表格编辑器。本页显示了业主抽签的表格数据(二维数组数据)的显示方法。 显示画面和整个程序如下所示。使用ListView_GetSubItemRect宏的话,就不需要getRect函数了。 当nCol的…...

深度学习本科课程 实验5 循环神经网络

循环神经网络实验 任务内容 理解序列数据处理方法&#xff0c;补全面向对象编程中的缺失代码&#xff0c;并使用torch自带数据工具将数据封装为dataloader分别采用手动方式以及调用接口方式实现RNN、LSTM和GRU&#xff0c;并在至少一种数据集上进行实验从训练时间、预测精度、…...

Redis篇之过期淘汰策略

一、数据的过期策略 1.什么是过期策略 Redis对数据设置数据的有效时间&#xff0c;数据过期以后&#xff0c;就需要将数据从内存中删除掉。可以按照不同的规则进行删除&#xff0c;这种删除规则就被称之为数据的删除策略&#xff08;数据过期策略&#xff09;。 2.过期策略-惰…...

【Kubernetes】kubectl top pod 异常?

目录 前言一、表象二、解决方法1、导入镜像包2、编辑yaml文件3、解决问题 三、优化改造1.修改配置文件2.检查api-server服务是否正常3.测试验证 总结 前言 各位老铁大家好&#xff0c;好久不见&#xff0c;卑微涛目前从事kubernetes相关容器工作&#xff0c;感兴趣的小伙伴相互…...

前后端分离项目:前端的文件夹应该叫什么名字,后端呢

在前后端分离的项目中&#xff0c;为了提高项目的可读性和易管理性&#xff0c;给前端和后端的文件夹选择合适的名字是很重要的。这里提供一些建议&#xff0c;但请记住&#xff0c;最终的命名应该根据你的团队习惯、项目特性以及可能的公司规定来决定。 ### 前端文件夹命名建…...

2024.2.6

1.现有无序序列数组为23,24,12,5,33,5347&#xff0c;请使用以下排序实现编程 函数1:请使用冒泡排序实现升序排序 函数2:请使用简单选择排序实现升序排序 函数3:请使用快速排序实现升序排序 函数4:请使用插入排序实现升序排序 #include<stdio.h> #include<string.h&g…...

如何在 Microsoft Azure 上部署和管理 Elastic Stack

作者&#xff1a;来自 Elastic Osman Ishaq Elastic 用户可以从 Azure 门户中查找、部署和管理 Elasticsearch。 此集成提供了简化的入门体验&#xff0c;所有这些都使用你已知的 Azure 门户和工具&#xff0c;因此你可以轻松部署 Elastic&#xff0c;而无需注册外部服务或配置…...

在Visual Studio中引用和链接OpenSceneGraph (OSG) 库

在Visual Studio中引用和链接OpenSceneGraph (OSG) 库&#xff0c;按照以下步骤操作&#xff1a; 构建或安装OSG库 下载OpenSceneGraph源代码&#xff08;如3.0版本&#xff09;并解压。使用CMake配置项目&#xff0c;为Visual Studio生成解决方案文件。通常您需要设置CMake中的…...

[缓存] - Redis

0.为什么要使用缓存&#xff1f; 用缓存&#xff0c;主要有两个用途&#xff1a;高性能、高并发。 1. 高性能 尽量使用短key 不要存过大的数据 避免使用keys *&#xff1a;使用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是一种集成学习方法&#xff0c;AdaBoost是Boosting算法中的一种具体实现。 Boosting方法的核心思想在于将多个弱分类器组合成一个强分类器。这些弱分类器通常是简单的模型&#xff0c;比如决策树&#xff0c;它们在训练过程中的错误会被后续的弱分类器所修正。Boosti…...

基于 elasticsearch v8 的 CRUD 操作及测试用例

基于 elasticsearch v8 的 CRUD 操作及测试用例 https://github.com/chenshijian73-qq/go-es/tree/main...

深度学习的新进展:解析技术演进与应用前景

深度学习的新进展&#xff1a;解析技术演进与应用前景 深度学习&#xff0c;作为人工智能领域的一颗璀璨明珠&#xff0c;一直以来都在不断刷新我们对技术和未来的认知。随着时间的推移&#xff0c;深度学习不断迎来新的进展&#xff0c;这不仅推动了技术的演进&#xff0c;也…...

【第二届 Runway短视频创作大赛】——截至日期2024年03月01日

短视频创作大赛 关于AI Fil&#xff4d; Festival竞赛概况参加资格报名期间报名方法 提交要求奖品附录 关于AI Fil&#xff4d; Festival 2022年成立的AIFF是一个融合了最新AI技术于电影制作中的艺术和艺术家节日&#xff0c;让我们得以一窥新创意时代的风采。从众多参赛作品中…...

UniApp 快速上手与深度学习指南

一、UniApp 简介 UniApp 是中国DCloud公司研发的一款创新的跨平台应用开发框架,它基于广受欢迎的前端开发库Vue.js,旨在解决多端适配和快速开发的问题。通过UniApp,开发者能够采用一套统一的代码结构、语法和API来构建应用程序,从而实现真正意义上的“一次编写,到处运行”…...

10个简单有效的编辑PDF文件工具分享

10个编辑PDF文件工具作为作家、编辑或专业人士&#xff0c;您可能经常发现自己在处理 PDF 文件。无论您是审阅文档、创建报告还是与他人共享工作&#xff0c;拥有一个可靠的 PDF 编辑器供您使用都非常重要。 10个简单适用的编辑PDF文件工具 在本文中&#xff0c;我们将介绍当今…...

电力负荷预测 | 基于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脚本 &#xff08;ntfs.sh脚本内容在本文最后面&#xff09; ntfs.sh脚本可以将Mac系统(苹果M系芯片)上的NTFS硬盘改成可读写的挂载方式&#xff0c;从而可以直接往NTFS硬盘写入数据。此脚本免费&#xff0c;使用过程中无需下载任何收费软件。…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...