漫画图解 Go 并发编程之:Channel

当谈到并发时,许多编程语言都采用共享内存/状态模型。然而,Go 通过实现 Communicating Sequential Processes(CSP)而与众不同。在 CSP 中,程序由不共享状态的并行处理器组成;相反,他们使用 Channel 来沟通和同步他们的行动。因此,对于有兴趣采用 Go 的开发人员来说,理解 Channel 的工作原理变得至关重要。在本文中,我将使用地鼠经营他们想象中的咖啡馆的令人愉快的类比来说明 Channel ,因为我坚信人类是更好的视觉学习者。

场景

Partier, Candier, Stringer 三人正在经营一家咖啡馆。鉴于制作咖啡比接受订单需要更多时间,Partier 将协助接受顾客的订单,然后将这些订单传递到厨房,由 Candier 和 Stringer 准备咖啡。

Gopher’s Cafe

无缓冲 Channels

最初,咖啡馆以最简单的方式运营:每当收到新订单时,Partier 都会将订单放入 Channel 中,并等到 Candier 或 Stringer 接受后才接受任何新订单。 Partier 和厨房之间的通信是通过使用 ch := make(chan Order) 创建的无缓冲 Channel 来实现的。当 Channel 中没有挂单时,即使 Stringer 和 Candier 都准备好接受新订单,它们也会保持空闲状态并等待新订单到达。

Unbuffered channel

当收到新订单时,Partier 将其放入 Channel 中,使 Candier 或 Stringer 可以接受该订单。但是,在继续接受新订单之前,Partier 必须等待后厨的两位工作者(Candier, Stringer)的其中一个从 Channel 中检索并获取订单。

20240109115309

由于 Candier 和 Stringer 都可以接受新订单,因此他们中的任何一个都会立即接受新订单。但是,无法保证或预测收到订单的具体收件人。 Stringer 和 Candier 之间的选择是不确定的,它取决于调度和 Go 运行时的内部机制等因素。假设 Candier 收到了第一个订单。

20240109115454

Candier 处理完第一个订单后,又回到等待状态。如果没有新订单到达,Candier 和 Stringer 这两个工作人员将保持空闲状态,直到 Partier 将另一个订单放入通道中供他们处理。

20240109115534

当新订单到达时,Stringer 和 Candier 都可以处理它。即使 Candier 刚刚处理了上一个订单,接收新订单的具体工人仍然是不确定的。在这种情况下,假设 Candier 再次被分配了第二个订单。

Distributing messages in channel is non-deterministic

新订单 order3 到达,Candier 当前正忙于处理 order2,她没有在队列中等待 order := <-ch,Stringer 成为唯一可以接收 order3 的工作人员。因此,他会得到它。

20240109115714

在 order3 发送到 Stringer 后,order4 立即到达。此时,Stringer 和 Candier 都已忙于处理各自的订单,没有人可以接收 order4。由于 Channel 没有缓冲,因此将 order4 放入其中会阻塞 Partier,直到 Stringer 或 Candier 可以接受 order4。这种情况值得特别注意,因为我经常看到人们对无缓冲通道(使用 make(chan order) 或 make(chan order, 0) 创建)和缓冲区大小为 1 的通道(使用 make(chan order, 1) 创建)感到困惑)。因此,他们错误地期望 ch <- order4 立即完成并接收 order5。如果您也是这么想的,我在 Go Playground 上创建了一个片段来帮助您纠正您的误解 Go Playground - The Go Programming Language。

20240109120009

带缓冲的 Channel

无缓冲通道可以工作,但是它限制了整体吞吐量。如果可以接受更多订单并在后端(厨房)按顺序处理它们,那就更好了。这可以通过缓冲通道来实现。现在,即使 Stringer 和 Candier 忙于处理订单,Partier 仍然可以在通道中留下新订单,并在通道未满的情况下继续接受其他订单,例如最多 3 个挂单。

20240109120242

通过引入缓冲 Channel,咖啡馆增强了处理更多订单的能力。然而,仔细选择适当的缓冲区大小以维持客户合理的等待时间至关重要。毕竟,没有顾客愿意忍受过长的等待。有时,拒绝新订单可能比接受新订单但无法及时履行订单更容易接受。此外,在临时容器化 (Docker) 应用程序中使用缓冲 Channel 时务必谨慎,因为容器会随机重启,在这种情况下从 Channel 恢复消息可能是一项具有挑战性的任务,甚至近乎不可能。

Channels vs Blocking Queues

尽管本质上不同,Java 中的 Blocking Queue 用于线程之间的通信,而 Go 中的 Channel 用于 Goroutine 的通信,BlockingQueue 和 Channel 的行为有些相似。如果你熟悉BlockingQueue,理解 Channel 肯定会很容易。

常见使用场景

Channel 是 Go 应用程序中一项基本且广泛使用的功能,可用于多种用途。Channel 的一些常见用例包括:

  • Goroutine 通信:Channel 支持不同 Goroutine 之间的消息交换,允许它们进行协作,而无需直接共享状态。
  • 工作池:如上例所示,Channel 通常用于管理工作池,其中多个相同的工作者处理来自共享Channel 的传入任务。
  • 扇出、扇入:Channel 用于扇出、扇入模式,其中多个 Goroutine(扇出)执行工作并将结果发送到单个 Channel,而另一个 Goroutine(扇入)消耗这些结果。
  • 超时和截止日期:Channel与 select 语句结合可用于处理超时和截止日期,确保程序可以优雅地处理延迟并避免无限期的等待。

我将在其他文章中更详细地探讨 Channel 的不同用法。然而,现在,让我们通过实现上述咖啡馆场景并见证渠道如何运作来结束这篇介绍性博客。我们将探讨 Partier、Candier 和 Stringer 之间的交互,观察 Channel 如何促进它们之间的顺畅沟通和协调,从而实现咖啡馆内高效的订单处理和同步。

Show me your code!

package mainimport ("fmt""log""math/rand""sync""time"
)func main() {ch := make(chan order, 3)wg := &sync.WaitGroup{} // More on WaitGroup another daywg.Add(2)go func() {defer wg.Done()worker("Candier", ch)}()go func() {defer wg.Done()worker("Stringer", ch)}()for i := 0; i < 10; i++ {waitForOrders()o := order(i)log.Printf("Partier: I %v, I will pass it to the channel\n", o)ch <- o}log.Println("No more orders, closing the channel to signify workers to stop")close(ch)log.Println("Wait for workers to gracefully stop")wg.Wait()log.Println("All done")
}func waitForOrders() {processingTime := time.Duration(rand.Intn(2)) * time.Secondtime.Sleep(processingTime)
}func worker(name string, ch <-chan order) {for o := range ch {log.Printf("%s: I got %v, I will process it\n", name, o)processOrder(o)log.Printf("%s: I completed %v, I'm ready to take a new order\n", name, o)}log.Printf("%s: I'm done\n", name)
}func processOrder(_ order) {processingTime := time.Duration(2+rand.Intn(2)) * time.Secondtime.Sleep(processingTime)
}type order intfunc (o order) String() string {return fmt.Sprintf("order-%02d", o)
}

您可以复制此代码,对其进行调整并在 IDE 上运行它,以更好地了解通道的工作原理。

本文译自:https://medium.com/stackademic/go-concurrency-visually-explained-channel-c6f88070aafa

20240109165306

欢迎加我好友,交流可观测性相关话题或了解我们的商业产品,我的微信号:picobyte,加好友请备注您的公司和姓名 🤝

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

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

相关文章

Spark如何用累加器Accumulator收集日志

Spark如何用累加器Accumulator收集日志 Accumulator如何使用Accumulator收集日志 Spark任务的实际运算是交由众多executor来执行的&#xff0c;如果再执行算子内部打印日志&#xff0c;是需要到对应的executor上才能看到。当不知道对应executor的情况下就需要挨个查询日志&…

为什么pgsql(内关联查询或者with字句时)会导致索引失效

1、在PostgreSQL中&#xff0c;内关联查询可能导致索引失效的原因通常与查询的过滤条件和数据分布有关。 以下是一些可能导致索引失效的情况&#xff1a; 1、使用了函数或类型转换&#xff1a;当查询条件中对索引字段使用了任何计算、函数或类型转换时&#xff0c;这可能会阻止…

【零基础学习CAPL】文章合集

本专栏基于CAPL脚本介绍常用脚本示例&#xff0c;每个工程完整可用&#xff0c; 持续更新中… &#x1f697;【零基础学习CAPL】——CAN报文的发送&#xff08;单帧周期性发送&#xff09; &#x1f697;【零基础学习CAPL】——CAN报文的发送&#xff08;单帧按键触发&#x…

基于Redis实现短信登录

首先我们要思考一下利用redis来存储数据&#xff0c;那么到底使用哪种结构呢&#xff1f;由于存入的数据比较简单&#xff0c;我们可以考虑使用String&#xff0c;或者是使用哈希&#xff0c;如下图&#xff0c;如果使用String&#xff0c;同学们注意他的value&#xff0c;用多…

第4章 数据表示与特征工程

目录 1. 分类变量1.1 One-Hot编码&#xff08;虚拟变量&#xff09;检查字符串编码的分类数据 1.2 数字可以编码分类变量 2. 分箱、离散化、线性模型与树3. 交互特征与多相似特征4. 单变量非线性变换总结&#xff08;2~4&#xff09;5. 自动化特征选择5.1 单变量统计5.2 基于模…

C语言菜鸟入门·判断语句(if语句、if...else语句、嵌套if语句)详细介绍

目录 1. if语句 2. if...else语句 3. if...else if...else 语句 4. 嵌套if语句 C 语言把任何非零和非空的值假定为 true&#xff0c;把零或 null 假定为 false。 语句描述if语句一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。if...else语句一个 if 语句 后可跟…

Unity3d实现简单的战斗

使用u3d实现一个简单的战斗demo&#xff0c;记下学到的知识点&#xff0c;以备后查。 1.判断是否点中指定物体 if (Input.GetMouseButton(0)) {Ray ray Camera.main.ScreenPointToRay(Input.mousePosition);if (Physics.Raycast(ray, out RaycastHit hit)){//坐标转换Vector…

Flink问题解决及性能调优-【Flink rocksDB读写state大对象导致背压问题调优】

RocksDB是Flink中用于持久化状态的默认后端&#xff0c;它提供了高性能和可靠的状态存储。然而&#xff0c;当处理大型状态并频繁读写时&#xff0c;可能会导致背压问题&#xff0c;因为RocksDB需要从磁盘读取和写入数据&#xff0c;而这可能成为瓶颈。 遇到的问题 Flink开发…

Redis抓取数据到Logstash再推到Elasticsearch集群

一、安装Logstash 前面安装过Logstash了,不做解释直接跳过 参考:上一篇文章 二、配置Logstash 在logstash目录下,编辑我们之前的配置文件logstash.conf vim logstash.confinput、output字面意思,从redis去拿取数据,输出到Elasticsearch data_type:数据类型为list k…

世微AP5125 LED外置MOS降压恒流驱动IC 12-36V 9V 1A驱动方案

本品特点&#xff1a;宽输入电压范围&#xff1a;9V&#xff5e;100V ◆ 固定工作频率&#xff1a;140KHZ◆ 可设定电流范围&#xff1a;10mA&#xff5e;6000mA ◆ 内置抖频电路&#xff0c;降低对其他设备的 EMI 干扰◆ 平均电流模式采样&#xff0c;恒流精度更高◆ CS 电压…

136832-63-8,活细胞示踪剂CMFDA(绿色),5-氯甲基荧光素二醋酸酯,广泛应用于细胞追踪和标记实验中

136832-63-8&#xff0c;活细胞示踪剂CMFDA(绿色)&#xff0c;5-氯甲基荧光素二醋酸酯&#xff0c;CellTracker Green CMFDA&#xff0c;可以用于基因表达分析等实验中&#xff0c;广泛应用于细胞追踪和标记实验中 您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;1…

瑞芯微1808模型转换(onnx到rknn)环境配置过程

瑞芯微1808模型转换&#xff08;onnx → \to →rknn&#xff09;环境配置 阅读本解决方案前&#xff0c;请读者确保已经根据官方的相关教程【rknn_model_zoo/common/rknn_converter at v1.5.0 airockchip/rknn_model_zoo (github.com)】完成其他配置文件的修改&#xff0c;以…

c语言学习笔记之字符串库函数和逗号表达式

逗号表达式 #include <stdio.h>int main(){int a 10;int b 5;int c 6;int d (a 23,b a-4,c b2);printf("%d",d); }打印结果为: 逗号表达式,从左往右依次进行,将最后一个表达式的值赋值给变量. c语言字符串相关库函数 求字符串长度strlen长度不受限制的…

Python学习笔记——类型注解基础知识

Python是动态类型语言&#xff0c;使用变量时不需要做任何类型声明&#xff0c;这是Python相比其它语言的一个重要优势&#xff1a;它减少了我们的心智负担&#xff0c;让写代码变得更容易。尤其对于我们很多新手来说&#xff0c;“不用声明类型”无疑会让学Python这件事变得简…

【MySQL】创建用户时报错

目录 前言解决方法 前言 在使用mysql服务时&#xff0c;用root创建用户&#xff0c;出现如下错误 The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement 解决方法 在mysql服务中输入指令 flush privileges;重启mysqld服务器…

Qt开源版 vs 商业版 详细比较!!!!

简单整理Qt开源版与商业版有哪些差别&#xff0c;仅供参考。 简单对比 开源版商业版许可证大部分采用对商业使用不友好的LGPLv3具备商业许可证保护代码专有许可证相关大部分模块使用LGPLv3和部分模块使用GPL组成仅第三方开源组件使用Qt的其他许可证Qt模块功能支持支持技术支持…

海外云手机为什么吸引用户?

近年来&#xff0c;随着全球化的飞速发展&#xff0c;海外云手机逐渐成为各行各业关注的焦点。那么&#xff0c;究竟是什么让海外云手机如此吸引用户呢&#xff1f;本文将深入探讨海外云手机的三大吸引力&#xff0c;揭示海外云手机的优势所在。 1. 高效的社交媒体运营 海外云…

c#窗体捕捉方向键

方法1 实现方法参考代码&#xff1a; private void Form1_Load(object sender, EventArgs e){this.KeyPreview true;}protected override bool ProcessDialogKey(Keys keyData){if (keyData Keys.Left || keyData Keys.Right || keyData Keys.Up || keyData Keys.Down){s…

Kubernetes-资源限制

一、Pod资源限制 Kubernetes 对资源的限制实际上是通过 CGROUP 来控制的&#xff0c;CGROUP 是容器的一组用来控制内核如果运行进程的相关属性集合。针对内存、CPU、和各种设备都有对应的 CGROUP。 默认情况下&#xff0c;Pod 运行没有 CPU 和内存的限额。这意味着系统中任何 P…

工程经验分享 Incremental FastPitch

分享 NVIDIA 基于 GPU 的 TTS 解决方案介绍。 1. 基于 FastPitchHifi-GAN 的 Streaming TTS 效果优化 NVIDIA 在 TTS 领域也做了一些供大家参考的工作&#xff0c;例如提供了高效的流式 TTS 部署方案&#xff0c;利用 TensorRT 加速模型推理速度&#xff0c;并通过 Triton Inf…