使用通信顺序进程(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.mzph.cn/news/56861.shtml

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

相关文章

对标 GPT-4?科大讯飞刘庆峰:华为GPU技术能力已与英伟达持平

科大讯飞创始人、董事长刘庆峰在亚布力中国企业家论坛第十九届夏季高峰会上透露了关于自家大模型进展的一些新内容。刘庆峰认为&#xff0c;中国在人工智能领域的算法并没有问题&#xff0c;但是算力方面似乎一直被英伟达所限制。 以往的“百模大战”中&#xff0c;训练大型模型…

4. 池化层相关概念

4.1 池化层原理 ① 最大池化层有时也被称为下采样。 ② dilation为空洞卷积&#xff0c;如下图所示。 ③ Ceil_model为当超出区域时&#xff0c;只取最左上角的值。 ④ 池化使得数据由5 * 5 变为3 * 3,甚至1 * 1的&#xff0c;这样导致计算的参数会大大减小。例如1080P的电…

SQLmap使用

文章目录 利用sqlmap 注入得到cms网站后台管理员账密获取数据库名称获取cms数据库的表名获取users表中的字段&#xff08;内容&#xff09;获取username字段和password字段的内容 salmap破解psot请求数据包salmap获取getshell 利用sqlmap 注入得到cms网站后台管理员账密 获取数…

mysql--数据库的操作

数据库&#xff0c;是数据存储的最大单元。 1 创建数据库 create database mydatabase; 每次创建数据库的时候&#xff0c;都会多一个文件夹&#xff0c;关系型数据库是存储在磁盘当中的&#xff0c;所以这时候可以查看新建的数据库 2 指定字符集 MySQL中的字符集转换过程 制…

Django会话技术

文章目录 Cookie实践运行结果 CSRF防止CSRF Session实践 Cookie 理论上&#xff0c;一个用户的所有请求燥作都应该属于同一个会话&#xff0c;而另一个用户的所有请求操作则应该属于另一个会话&#xff0c;二者不能混淆&#xff0c;而web应用程序是使用HTTP协议传输数据的。HTT…

开始MySQL之路—— DDL语法、DML语法、DQL语法基本操作详解

DDL语法 DDL&#xff08;Data Definition Language&#xff09; 数据定义语言&#xff0c;该语言部分包括以下内容。 对数据库的常用操作 对表结构的常用操作 修改表结构 对数据库的常用操作 1: 查看当前所有的数据库 show databases; 2&#xff1a;创建数据库 create dat…

SAP-FI-会计凭字段替代OBBH

会计凭证替代OBBH 业务&#xff1a;文本必须等于某个字段的值&#xff0c;例如凭证日期 关闭确认功能&#xff0c;输入OBBH 双击“替代”进入功能配置&#xff0c;或者用GGB1&#xff0c;用GGB1的功能更多。 点击行项目&#xff0c;点击“新建替换”保存 点击新建YXL7331,点击…

生态经济学领域里的R语言机器学(数据的收集与清洗、综合建模评价、数据的分析与可视化、数据的空间效应、因果推断等)

近年来&#xff0c;人工智能领域已经取得突破性进展&#xff0c;对经济社会各个领域都产生了重大影响&#xff0c;结合了统计学、数据科学和计算机科学的机器学习是人工智能的主流方向之一&#xff0c;目前也在飞快的融入计量经济学研究。表面上机器学习通常使用大数据&#xf…

Android开发Demo:TextView同时显示图片和文本、同时显示多种颜色

一、预期效果 1、在一个Android TextView控件之中&#xff0c;同时显示文本和图片&#xff0c;如下图所示&#xff0c;文本之间掺夹着一张或多张图片。 2、在一个Android TextView控件之中&#xff0c;同时显示多种颜色&#xff0c;如下图所示&#xff0c;一条文本显示了两种不…

常见API架构介绍

常见API架构介绍 两个服务间进行接口调用&#xff0c;通过调用API的形式进行交互&#xff0c;这是常见CS架构实现的模式&#xff0c;客户端通过调用API即可使用服务端提供的服务。相较于SPI这种模式&#xff0c;就是服务端只规定服务接口&#xff0c;但具体实现交由第三方或者自…

AcWing 898. 数字三角形 (每日一题)

大家好 我是寸铁 希望这篇题解对你有用&#xff0c;麻烦动动手指点个赞或关注&#xff0c;感谢您的关注 注意 像数组下标出现i-1的&#xff0c;在循环的时候从i1开始。 关于0x3f3f3f3f和Integer.MAX_VALUE 0x3f3f3f3f:1061109567 Integer.MAX_VALUE:2147483647 在选用Integ…

(四)k8s实战-服务发现

一、Service 1、配置文件 apiVersion: v1 kind: Service metadata:name: nginx-svclabels:app: nginx-svc spec:ports:- name: http # service 端口配置的名称protocol: TCP # 端口绑定的协议&#xff0c;支持 TCP、UDP、SCTP&#xff0c;默认为 TCPport: 80 # service 自己的…

win10电脑记事本在哪里?电脑记事本如何查看字数?

在日常工作中&#xff0c;我们会遇到许多需要记录的信息和事项&#xff0c;而使用电脑记事本工具可以帮助我们方便地保存、管理这些内容。无论是记录工作会议的要点、制定工作计划&#xff0c;还是记录灵感和创意&#xff0c;电脑记事本都是非常实用的工具。 那么win10电脑记事…

springboot服务注册到Eureka,端口总是默认8080,自己配置端口不生效

这段时间接手了一个公司的老项目&#xff0c;用的是SpringCloud&#xff0c;在我用的时候突然发现有一个服务&#xff0c;注册到Eureka后&#xff0c;界面显示的端口和实际Ribbon调用的实例端口是不一致的&#xff0c;后来我自己写了个端口获取了一下所有的实例信息&#xff0c…

卷积神经网络——中篇【深度学习】【PyTorch】【d2l】

文章目录 5、卷积神经网络5.5、经典卷积神经网络&#xff08;LeNet&#xff09;5.5.1、理论部分5.5.2、代码实现 5.6、深度卷积神经网络&#xff08;AlexNet&#xff09;5.6.1、理论部分5.6.2、代码实现 5.7、使用块的网络&#xff08;VGG&#xff09;5.7.1、理论部分5.7.2、代…

第59步 深度学习图像识别:误判病例分析(TensorFlow)

基于WIN10的64位系统演示 一、写在前面 本期内容对等于机器学习二分类系列的误判病例分析&#xff08;传送门&#xff09;。既然前面的数据可以这么分析&#xff0c;那么图形识别自然也可以。 本期以mobilenet_v2模型为例&#xff0c;因为它建模速度快。 同样&#xff0c;基…

基于LOF算法的异常值检测

目录 LOF算法简介Sklearn官网LOF算法应用实例1Sklearn官网LOF算法应用实例2基于LOF算法鸢尾花数据集异常值检测读取数据构造数据可视化&#xff0c;画出可疑异常点LOF算法 LOF算法简介 LOF异常检测算法是一种基于密度的异常检测算法&#xff0c;基于密度的异常检测算法主要思想…

windows下Mysql安装配置教程

Mysql下载 在官网下载mysql community Server https://dev.mysql.com/downloads/mysql/ 可以选择下载压缩包或者MSI安装程序 使用压缩包安装 MySQL 压缩包安装通常需要以下步骤&#xff1a; 1. 下载 MySQL 安装包 你可以从 MySQL 官网上下载适合你系统的 MySQL 安装包&am…

nvm安装electron开发与编译环境

electron总是安装失败&#xff0c;下面说一下配置办法 下载软件 nvm npmmirror 镜像站 安装nvm 首先最好卸载node&#xff0c;不卸载的话&#xff0c;安装nvm会提示是否由其接管&#xff0c;保险起见还是卸载 下载win中的安装包 配置加速节点nvm node_mirror https://npmmi…

Linux学习之DNS服务的原理

DNS服务一些理论 域名系统&#xff08;Domain Name System&#xff0c;DNS&#xff09;是互联网的核心应用服务&#xff0c;可以通过IP地址查询到域名&#xff0c;也可以通过域名查询到IP地址。 FQDN&#xff08;Full Qualified Domain Name&#xff09;是完全限定域名&#xf…