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

在并发编程中,许多编程语言采用共享内存/状态模型。然而,Go 通过实现 通信顺序进程(CSP)模型来区别于众多。在CSP中,程序由不共享状态的并行进程组成;相反,它们通过通道进行通信和同步操作。因此,对于有兴趣采用Go的开发人员来说,理解通道的工作原理变得至关重要。在本文中,我将使用愉快的比喻,描述Gophers运营他们的想象中的咖啡馆,因为我坚信人类更适合通过视觉学习。

场景

Partier、Candier 和 Stringer 正在经营一家咖啡馆。鉴于制作咖啡比接受订单需要更多时间,Partier 将负责从顾客那里接受订单,然后将这些订单传递给厨房,Candier 和 Stringer 将准备咖啡。

011465bd7bd2d53ab38812c98ace599b.png

无缓冲通道

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

7221ab876b327e9d858f1387f3200fd2.png

无缓冲通道

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

715040732a52945761ff310d468b4ed1.png

由于 Stringer 和 Candier 都可以接受新订单,所以新订单将立即被其中一个接受。然而,具体的接收者无法保证或预测。Stringer 和 Candier 之间的选择是不确定的,它取决于诸如调度和Go运行时的内部机制等因素。假设 Candier 得到了第一个订单。

a9f9fa268d0cc10377aec091013be776.png

在 Candier 完成处理第一个订单后,她返回等待状态。如果没有新订单到达,Candier 和 Stringer 两个工作者都将保持空闲状态,直到 Partier 再次将订单放入通道中以供处理。

d82446bb2d56589d2bd2b8ca402609bf.png

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

ed49c996a3e42661c036caa009451114.png

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

1fda92f75f4155599e1d68536a3a8dd2.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 在被阻塞之前接受 order5。如果您也是这样认为的,我已经在Go Playground上创建了一个代码片段,以帮助您纠正这种误解:https://go.dev/play/p/shRNiDDJYB4。

44cd09c341c94d75a1a14a96b572c7a2.png

带缓冲通道

无缓冲通道可以工作,但它限制了整体吞吐量。如果他们只接受一些订单来按顺序在后台(厨房)处理它们,那会更好。这可以通过带缓冲通道实现。现在,即使 Stringer 和 Candier 忙于处理他们的订单,只要通道不满,即可让 Partier 将新订单放入通道中,并继续接受额外的订单,例如最多3个未处理订单。

742e9ec62cf1d12d32ce2aa78c0e5684.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{} // 更多关于 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上进行微调并运行,以更好地理解通道的工作方式。

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

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

相关文章

wireshark抓包

Wireshark是非常流行的网络封包分析软件&#xff0c;可以截取各种网络数据包&#xff0c;并显示数据包详细信息。常用于开发测试过程各种问题定位。本文主要内容包括&#xff1a; 1、Wireshark软件下载和安装以及Wireshark主界面介绍。 2、WireShark简单抓包示例。通过该例子学…

最新绕过目标域名CDN进行信息收集技术

绕过目标域名CDN进行信息收集 1&#xff0e;CDN简介及工作流程 CDN&#xff08;Content Delivery Network&#xff0c;内容分发网络&#xff09;的目的是通过在现有的网络架构中增加一层新的Cache&#xff08;缓存&#xff09;层&#xff0c;将网站的内容发布到最接近用户的网…

ubuntu下自启动设置,为了开机自启动launch文件

1、书写sh脚本文件 每隔5秒钟启动一个launch文件&#xff0c;也可以直接在一个launch文件中启动多个&#xff0c;这里为了确保启动顺利&#xff0c;添加了一些延时 #! /bin/bash ### BEGIN INIT sleep 5 gnome-terminal -- bash -c "source /opt/ros/melodic/setup.bash…

uniapp - 全平台兼容实现上传图片带进度条功能,用户上传图像到服务器时显示上传进度条效果功能(一键复制源码,开箱即用)

效果图 uniapp小程序/h5网页/app实现上传图片并监听上传进度,显示进度条完整功能示例代码 一键复制,改下样式即可。 全部代码 记得改下样式,或直接

MyBatis的基本入门及Idea搭建MyBatis坏境且如何一步骤实现增删改查(CRUD)---详细介绍

一&#xff0c;MaBatis是什么&#xff1f; 首先是一个开源的Java持久化框架&#xff0c;它可以帮助开发人员简化数据库访问的过程并提供了一种将SQL语句与Java代码进行解耦的方式&#xff0c;使得开发人员可以更加灵活地进行数据库操作。 1.1 Mabatis 受欢迎的点 MyBatis不仅是…

使用CSS的@media screen 规则为不同的屏幕尺寸设置不同的样式(响应式图片布局)

当你想要在不同的屏幕尺寸或设备上应用不同的CSS样式时&#xff0c;可以使用 media 规则&#xff0c;特别是 media screen 规则。这允许你根据不同的屏幕特性&#xff0c;如宽度、高度、方向等&#xff0c;为不同的屏幕尺寸设置不同的样式。 具体来说&#xff0c;media screen…

【Spring MVC】

目录 &#x1f36e;1 什么是 MVC &#xff1f; &#x1f381;2 Spring MVC 的连接 &#x1f358;2.1 RequestMapping 实现 POST 和 GET 请求 &#x1f963;2.2 GetMapping 只支持 GET 请求 &#x1fad6;2.3 PostMapping 只支持 POST 请求 &#x1f36c;3 Spring MVC 获取参数的…

Spring复习:(56)PropertySourcePlaceholderConfigurer的工作原理

PropertySourcePlaceholderConfigurer的用途&#xff1a;通过配置文件&#xff08;比如.properties文件&#xff09;给bean设置属性&#xff0c;替代属性占位符 示例&#xff1a; 属性配置文件 spring.datasource.urljdbc:mysql://xxx.xxx.xxx.xxx/test spring.datasource.us…

【数仓建设系列之三】数仓建模方式及如何评估数仓完善性

【数仓建设系列之三】数仓建模方式及如何评估数仓完善性 上篇文章我们对数仓的分层架构及核心概念做了简单介绍&#xff0c;同时也指明DW层是数仓建模的核心层。本篇文章&#xff0c;将详细从常见的维度模型建设手段及如何评估数仓建设的完善性展开讨论。 一、数仓维度建模 ​…

Vue2向Vue3过度核心技术进阶语法

目录 1 v-model简化代码1.目标&#xff1a;2.如何简化&#xff1a;3.代码示例 2 sync修饰符1.作用2.场景3.本质4.语法5.代码示例6.总结 3 ref和$refs1.作用2.特点&#xff1a;3.语法4.注意5.代码示例 4 异步更新 & $nextTick1.需求2.代码实现3.问题4.解决方案 1 v-model简化…

Scikit-learn强化学习代码批注及相关练习

一、游戏介绍 木棒每保持平衡1个时间步&#xff0c;就得到1分。每一场游戏的最高得分为200分每一场游戏的结束条件为木棒倾斜角度大于41.8或者已经达到200分。最终获胜条件为最近100场游戏的平均得分高于195。代码中env.step&#xff08;&#xff09;&#xff0c;的返回值就分…

openresty安装与网站发布

文章目录 安装依赖下载安装包解压安装包安装启动nginx配置环境变量配置开机启动发布静态网站 OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台&#xff0c;其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动…

Docker(一)-安装、架构、业务开发常用命令、Dockerile、镜像卷、镜像仓库

基于业务开发使用Docker Docker是一个开源的容器引擎&#xff0c;它有助于更快地交付应用。Docker可将应用程序和基础设施层隔离&#xff0c;并且能将基础设施当作程序一样进行管理。使用 Docker可更快地打包、测试以及部署应用程序&#xff0c;并可以缩短从编写到部署运行代码…

【校招VIP】产品思维分析之面试新的功能点设计

考点介绍&#xff1a; 这种题型是面试里出现频度最高&#xff0c;也是难度最大的一种&#xff0c;需要面试者对产品本身的功能、扩展性以及行业都有一定的了解。而且分析时间较短&#xff0c;需要一定的产品能力和回答技巧。 『产品思维分析之面试新的功能点设计』相关题目及解…

服务注册中心 Eureka

服务注册中心 Eureka Spring Cloud Eureka 是 Netflix 公司开发的注册发现组件&#xff0c;本身是一个基于 REST 的服务。提供注册与发现&#xff0c;同时还提供了负载均衡、故障转移等能力。 Eureka 有 3 个角色 服务中心&#xff08;Eureka Server&#xff09;&#xff1a;…

Ensp+Wireshark+VirtualBox+WinPcap

软件下载 [名称]&#xff1a;Ensp及辅助程序 [大小]&#xff1a;830.65MB [语言]&#xff1a;Chinese [安装环境]&#xff1a;Win7/Win8/Win10 [下载链接]&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1KbypgxAGQy07ijSAj3SvsQ 提取码&#xff1a;ly88 软件介…

服务器数据恢复-EVA存储磁盘故障导致存储崩溃的数据恢复案例

EVA系列存储是一款以虚拟化存储为实现目的的中高端存储设备。EVA存储中的数据在EVA存储设备工作过程中会不断进行迁移&#xff0c;如果运行的任务比较复杂&#xff0c;EVA存储磁盘负载加重&#xff0c;很容易出现故障的。EVA存储通过大量磁盘的冗余空间和故障后rss冗余磁盘动态…

Netty核心源码解析(三)--NioEventLoop

NioEventLoop介绍 NioEventLoop继承SingleThreadEventLoop,核心是一个单例线程池,可以理解为单线程,这也是Netty解决线程并发问题的最根本思路--同一个channel连接上的IO事件只由一个线程来处理,NioEventLoop中的单例线程池轮询事件队列,有新的IO事件或者用户提交的task时便执…

第二讲Java基本语法(变量、数据类型、运算符)

一、前言导读 上一讲,我们安装java的开发工具idea,并且简单介绍如何使用,初步认识了Java的helloworld,我们写了第一行代码,有了初步的印象,接下来我们将真正展开对于java的了解,从这一讲开始,请大家做好笔记,改背的背。为什么说Java是一门编程语言呢,主要是他跟英语一…

php 多维数组排序,根据某一列排序(array_multisort()和array_column()联用)

array_multisort()和array_column()联用效果直接叠满,11>100 先来看下两个函数的介绍和用法 array_column(): 一般模式,不需要其中字段作为id,只需要提取val值 <?php // 可能从数据库中返回数组 $a [[id > 5698, first_name > Peter, last_name > G…