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

以可视化方式解释 Go 并发 - 通道

在并发编程中,许多编程语言采用共享内存/状态模型。然而,Go 通过实现 通信顺序进程 (CSP) 区别于众多语言。在 CSP 中,一个程序由并行的进程组成,这些进程不共享状态,而是使用通道进行通信和同步它们的操作。因此,对于有意采用 Go 的开发人员来说,理解通道的工作原理变得至关重要。在本文中,我将使用 Gopher 运行他们的虚构咖啡馆的可爱比喻来阐述通道,因为我坚信人类更容易通过视觉学习。

情景

Partier、Candier 和 Stringer 经营一家咖啡馆。由于制作咖啡需要比接受订单更多的时间,Partier 将协助接受客户的订单,然后将这些订单传递到厨房,Candier 和 Stringer 在那里制作咖啡。

407a7ee78158179ec43e576ab86bfa5d.png

Gopher's Cafe

无缓冲通道

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

a7a89150e73f79a5f2a009a8aaf39f60.png

无缓冲通道

当收到新订单时,Partier 将其放入通道中,使订单可以被 Candier 或 Stringer 之一接受。但是,在继续接受新订单之前,Partier 必须等待其中一个从通道中获取订单。

e07aa24dc42c12858855596399702a50.png

由于 Stringer 和 Candier 都可以接受新订单,因此订单将立即被其中一个接受。但是,不能保证或预测哪个具体的接收者会获取订单。在 Stringer 和 Candier 之间的选择是非确定性的,它依赖于诸如调度和 Go 运行时的内部机制等因素。假设 Candier 获取了第一个订单。

2934935331472e841430139ae0e66763.png

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

c00dd14bc53dfd243aa9f50f62734c6f.png

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

4ebd334a682903ef86f189679957060b.png

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

74c7465da2d4a72141ca1eabc9876f5b.png

在将 order3 发送给 Stringer 后不久,order4 到达。此时,Stringer 和 Candier 已经忙于处理各自的订单,没有人可以接收 order4。由于通道没有缓冲,将 order4 放入通道会阻塞 Partier,直到 Stringer 或 Candier 可以接收 order4 为止。这种情况值得特别注意,因为我经常看到人们对无缓冲通道(使用 make(chan order) 或 make(chan order, 0) 创建)和具有缓冲大小为 1 的通道(使用 make(chan order, 1) 创建)感到困惑。因此,他们错误地期望 ch <- order4 立即完成,允许 Partier 在 ch <- order5 处被阻塞之前接受 order5。如果您也是这样认为的,我已经在 Go Playground 上创建了一个代码片段,以帮助您纠正您的误解 https://go.dev/play/p/shRNiDDJYB4。

2c4426f6a9faafc4aaa93b93427e0c7c.png

带缓冲通道

无缓冲通道是有效的,但它限制了总吞吐量。如果他们只接

受一些订单以便在后端(厨房)顺序处理它们,那将更好。这可以通过使用带缓冲通道来实现。现在,即使 Stringer 和 Candier 忙于处理他们的订单,只要通道不满,例如最多 3 个待处理订单,Partier 仍然可以将新订单放入通道并继续接受其他订单。

4fc6afe236daf3e0f1f953a43aacaa28.png
1*TCrrdUaa7XPcmRSDIO7xEQ.png

引入带缓冲通道后,咖啡馆增强了处理更多订单的能力。然而,选择适当的缓冲区大小以保持客户合理的等待时间非常重要。毕竟,没有客户想忍受过长的等待时间。有时,拒绝新订单可能比接受新订单但无法及时完成它们更可接受。此外,在使用带缓冲通道的瞬时容器化(Docker)应用程序时要小心,因为预计会随机重启,在这种情况下,从通道中恢复消息可能是一项具有挑战性甚至不可能的任务。

通道 vs 阻塞队列

尽管基本上不同,Java 中的阻塞队列用于线程之间的通信,而 Go 中的通道用于 Goroutine 的通信,但阻塞队列和通道在某种程度上表现出相似之处。如果您熟悉阻塞队列,那么理解通道肯定会更容易。

常见用途

通道是 Go 应用程序中的基本和广泛使用的功能,可以用于各种用途。通道的一些常见用例包括:

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

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

演示代码

package mainimport ("fmt""log""math/rand""sync""time"
)func main() {ch := make(chan order, 3)wg := &sync.WaitGroup{}wg.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 上进行调整并运行,以更好地理解通道的工作原理。

相关系列文章:

使用通信顺序进程(CSP)模型的 Go 语言通道

Go并发可视化解释 - Select语句

相关文章:

以可视化方式解释 Go 并发 - 通道

在并发编程中&#xff0c;许多编程语言采用共享内存/状态模型。然而&#xff0c;Go 通过实现 通信顺序进程 (CSP) 区别于众多语言。在 CSP 中&#xff0c;一个程序由并行的进程组成&#xff0c;这些进程不共享状态&#xff0c;而是使用通道进行通信和同步它们的操作。因此&…...

kafka学习-生产者

目录 1、消息生产流程 2、生产者常见参数配置 3、序列化器 基本概念 自定义序列化器 4、分区器 默认分区规则 自定义分区器 5、生产者拦截器 作用 自定义拦截器 6、生产者原理解析 1、消息生产流程 2、生产者常见参数配置 3、序列化器 基本概念 在Kafka中保存的数…...

【Python】设计模式

设计模式分为三种类型&#xff0c;共23类。 创建型模式&#xff1a;单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。结构型模式&#xff1a;适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。行为型模式&#xff1a;模版方法模式、命令模…...

C++ 数字

C 数字 通常&#xff0c;当我们需要用到数字时&#xff0c;我们会使用原始的数据类型&#xff0c;如 int、short、long、float 和 double 等等。这些用于数字的数据类型&#xff0c;其可能的值和数值范围&#xff0c;我们已经在 C 数据类型一章中讨论过。 C 定义数字 我们已…...

code阶段——gitgitlab安装

在code阶段&#xff0c;我们需要将不同版本的代码存储到一个仓库中&#xff0c;常见的版本控制工具就是SVN或者Git&#xff0c;这里我们采用Git作为版本控制工具&#xff0c;GitLab作为远程仓库。 Git安装 https://git-scm.com/&#xff08;傻瓜式安装&#xff09; GitLab安…...

C 风格文件输入/输出---无格式输入/输出

C 标准库的 C I/O 子集实现 C 风格流输入/输出操作。 <cstdio> 头文件提供通用文件支持并提供有窄和多字节字符输入/输出能力的函数&#xff0c;而 <cwchar>头文件提供有宽字符输入/输出能力的函数。 无格式输入/输出 从文件流获取字符 std::fgetc, std::getc …...

Spring-MVC的文件上传下载,及插件的使用(让项目开发更节省时间)

目录 一、概述 ( 1 ) 介绍 ( 2 ) 讲述 二、上传 三、下载 四、jrebel的使用 五、多文件上传 给我们带来什么收获 一、概述 ( 1 ) 介绍 Spring MVC的文件上传下载是指在Spring MVC框架中实现文件的上传和下载功能。文件上传是指将本地计算机上的文件上传到服务器端…...

算法 数据结构 递归冒泡算法 java冒泡算法 优化递归冒泡 数据结构(九)

使用递归算法实现冒泡&#xff1a; package com.nami.algorithm.study.day06;import java.util.Arrays;/*** beyond u self and trust u self.** Author: lbc* Date: 2023-09-05 15:36* email: 594599620qq.com* Description: keep coding*/ public class BubbleSort2 {// p…...

【计算机视觉 | 目标检测】目标检测常用数据集及其介绍(十五)

文章目录 一、STN PLAD (STN Power Line Assets Dataset)二、Satlas三、Street Dataset四、UAVVaste五、UDA-CH (Unsupervised Domain Adaptation on Cultural Heritage)六、USB (Universal-Scale Object Detection Benchmark)七、VEDAI (Vehicle Detection in Aerial Imagery)…...

洛谷P8814:解密 ← CSP-J 2022 复赛第2题

【题目来源】https://www.luogu.com.cn/problem/P8814https://www.acwing.com/problem/content/4732/【题目描述】 给定一个正整数 k&#xff0c;有 k 次询问&#xff0c;每次给定三个正整数 ni&#xff0c;ei&#xff0c;di&#xff0c;求两个正整数 pi&#xff0c;qi&#xf…...

Flutter实现CombineExecutor进行多个异步分组监听,监听第一个异步执行的开始和最后一个异步执行结束时机。

1.场景 我们在调用接口时&#xff0c;很多时候会同时调用多个接口&#xff0c;接口都是异步执行&#xff0c;我们很难知道调用的多个接口哪个会最后执行完成&#xff0c;我们有时候需要对最后一个接口执行完成的时机监听&#xff0c;所以基于该需求&#xff0c;设计了CombineE…...

2023 年最新Java 毕业设计选题题目参考,500道 Java 毕业设计题目,值得收藏

大家好&#xff0c;我是程序员徐师兄&#xff0c;最近有很多同学咨询&#xff0c;说毕业设计了&#xff0c;不知道选怎么题目好&#xff0c;有哪些是想需要注意的。 确实毕设选题实际上对很多同学来说一个大坑&#xff0c; 每年挖坑给自己跳的人太多太多&#xff0c;选题选得好…...

Mac电脑其他文件占用超过一大半的内存如何清理?

mac的存储空间时不时会提示内存已满&#xff0c;查看内存占用比例最大的居然是「其他文件」&#xff0c;「其他文件」是Mac无法识别的格式文件或应用插件扩展等等...如果你想要给Mac做一次彻底的磁盘空间清理&#xff0c;首当其冲可先对「其他文件」下手&#xff0c;那么我们该…...

geopandas 笔记: datasets 数据集

geopandas 自带的几个数据集 1 世界各个国家 import geopandas as gpd import pandas as pdpd.set_option(display.max_rows,None) gpd.read_file(gpd.datasets.get_path(naturalearth_lowres)) pop_est人口数量continent国家所在的大陆name国家的名称iso_a3国家的三个字母的…...

长胜证券:三大拐点共振 看好智能驾驶新一轮行情

摘要 【长胜证券&#xff1a;三大拐点共振 看好智能驾驭新一轮行情】长胜证券研报指出&#xff0c;全球共振&#xff0c;国内智驾商场正迎来三大拐点&#xff1a;1&#xff09;技能上&#xff0c;“BEV Transformer数据闭环”新架构2023年开端上车&#xff0c;使得不依靠高精地…...

AIGC专栏5——EasyPhoto AI写真照片生成器 sd-webui插件介绍、安装与使用

AIGC专栏5——EasyPhoto AI写真照片生成器 插件安装与使用 学习前言源码下载地址技术原理储备&#xff08;SD/Control/Lora&#xff09;StableDiffusionControlNetLora EasyPhoto插件简介EasyPhoto插件安装安装方式一&#xff1a;Webui界面安装 &#xff08;需要良好的网络&…...

【Python程序设计】 工厂模式【07/8】

一、说明 我们探索数据工程中使用的设计模式 - 软件设计中常见问题的可重用解决方案。 以下文章是有关 Python 数据工程系列文章的一部分&#xff0c;旨在帮助数据工程师、数据科学家、数据分析师、机器学习工程师或其他刚接触 Python 的人掌握基础知识。 迄今为止&#xff0c;…...

PHP8的多维数组-PHP8知识详解

今天分享的是php8的数组中的多维数组&#xff0c;主要内容有&#xff1a;多维数组的概念、创建和输出二维数组、创建和输出三维数组。 1、多维数组的概念 多维数组是包含一个或多个数组的数组。在多维数组中&#xff0c;主数组中的每一个元素也可以是一个数组&#xff0c;子数…...

【【STM32--28--IO引脚的复用功能】】

STM32–28–IO引脚的复用功能 STM32的IO复用功能 何为复用? 我们先了解一下何为通用 IO端口的输入或输出是由GPIO外设控制&#xff0c;我们称之为通用 复用&#xff1a; IO端口的输入或者是输出是由其他非GPIO外设控制就像经常说的USART 由 DR寄存器进行输出 STM32的IO复用功…...

CodeJock Active-X / COM v22.1.0 Crack

CodeJock Active-X / COM v22.1.0--这个支持 Unicode 啦&#xff0c; Unicode Unicode 创建专业应用程序&#xff0c;其中包含一整套高度可定制的用户界面组件&#xff0c;包括 Visual Studio 风格的对接窗格和 Office 风格的功能区、工具栏和菜单&#xff0c;为您的应用程序…...

AI建站工具选型指南:一张表看懂怎么选,哪个适合你

AI建站工具选型指南&#xff1a;一张表看懂怎么选&#xff0c;哪个适合你痛点与目标&#xff1a;为什么选个工具这么难市面上的建站工具都宣传自己能“AI生成”“一键建站”&#xff0c;但你点进去一看&#xff0c;有的要自己拖模板&#xff0c;有的要自己写文案&#xff0c;有…...

AI人工智能未来发展趋势

当ChatGPT实现自然语言的深度交互&#xff0c;当AI机器人走进工厂车间&#xff0c;当智能算法助力疫苗研发提速&#xff0c;人工智能已从实验室的前沿探索&#xff0c;成为渗透社会各领域的核心生产力。当前&#xff0c;AI技术正处于从“弱智能”向“强智能”跨越的关键节点&am…...

Apple Silicon Mac原生Linux游戏体验:Asahi Linux驱动突破与实战指南

1. 项目概述&#xff1a;当Apple Silicon Mac遇见原生Linux游戏如果你和我一样&#xff0c;既是Mac用户&#xff0c;又对在Linux系统上折腾抱有热情&#xff0c;那么最近Asahi Linux项目的进展绝对会让你心跳加速。长久以来&#xff0c;在搭载Apple Silicon&#xff08;M1、M2、…...

从C代码到汇编:图解函数调用栈中rsp和rbp的“职责分工”

从C代码到汇编&#xff1a;图解函数调用栈中rsp和rbp的"职责分工" 在计算机程序的执行过程中&#xff0c;函数调用是最基础也最核心的概念之一。当我们从高级语言如C/C深入到汇编层面时&#xff0c;会发现函数调用的背后隐藏着一套精密的栈帧管理机制。本文将带您走进…...

科技早报晚报|2026年5月15日:本地大表分析、零 ETL 远程搜索与去中心化监控,今晚更值得跟进的 3 个技术机会

科技早报晚报&#xff5c;2026年5月15日&#xff1a;本地大表分析、零 ETL 远程搜索与去中心化监控&#xff0c;今晚更值得跟进的 3 个技术机会 一句话导读&#xff1a;上午那篇我已经写了空间感知、设备实验室和视频代理&#xff0c;今晚这轮我刻意换到另一条更贴近真实工程预…...

对比直接使用官方API体验Taotoken在稳定性与成本上的差异

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 对比直接使用官方API体验Taotoken在稳定性与成本上的差异 在将大模型能力集成到个人项目或小团队工作流中时&#xff0c;开发者通常…...

不止Keil5:VSCode+GCC也能玩转GD32单片机?手把手教你搭建轻量级开发环境

超越Keil5&#xff1a;用VSCodeGCC打造高效GD32开发环境 在嵌入式开发领域&#xff0c;Keil MDK长期以来一直是ARM架构单片机开发的主流选择。然而&#xff0c;随着现代开发工具的演进&#xff0c;越来越多的开发者开始寻求更轻量、更灵活且完全免费的替代方案。本文将带你探索…...

从零到自动化:手把手教你用nRF Connect搭建个人BLE设备测试流水线

从零到自动化&#xff1a;手把手教你用nRF Connect搭建个人BLE设备测试流水线 在物联网设备开发中&#xff0c;蓝牙低功耗(BLE)技术的测试验证一直是让开发者头疼的环节。传统手动测试不仅效率低下&#xff0c;还容易因人为因素导致结果不一致。对于资源有限的硬件创业团队或个…...

PCB线宽与电流关系详解:从原理到设计避坑指南

1. 项目概述&#xff1a;从一次烧板事故说起去年&#xff0c;我手头一个给电机驱动的小板子又冒烟了。排查了半天&#xff0c;发现不是芯片烧了&#xff0c;也不是电源接反了&#xff0c;问题出在一条给电机供电的电源走线上。那条线在板子上看着挺“粗壮”&#xff0c;但实际一…...

STFT音高迁移:C++实现音频变调不变速的核心原理与工程实践

1. 项目概述&#xff1a;音频处理的“时间魔法师”如果你玩过音乐制作或者做过音频分析&#xff0c;肯定遇到过这样的场景&#xff1a;一段人声录音的音调有点低&#xff0c;你想把它调高一点&#xff0c;但又不想改变它说话的速度和节奏感。或者反过来&#xff0c;一段背景音乐…...