首页 > 编程语言 >漫画图解 Go 并发编程之:Channel

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

时间:2024-01-30 12:11:17浏览次数:38  
标签:Candier 订单 Channel Partier Go 图解 Stringer

当谈到并发时,许多编程语言都采用共享内存/状态模型。然而,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 上创建了一个片段来帮助您纠正您的误解 https://go.dev/play/p/shRNiDDJYB4

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 main

import (
  "fmt"
  "log"
  "math/rand"
  "sync"
  "time"
)

func main() {
  ch := make(chan order, 3)

  wg := &sync.WaitGroup{} // More on WaitGroup another day
  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.Second
  time.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.Second
  time.Sleep(processingTime)
}

type order int

func (o order) String() string {
  return fmt.Sprintf("order-%02d", o)
}

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

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

20240109165306

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

标签:Candier,订单,Channel,Partier,Go,图解,Stringer
From: https://www.cnblogs.com/ulricqin/p/17996828

相关文章

  • GoLand 2023:专注性能,提升Go开发者的工作效率 mac/win版
    JetBrainsGoLand2023是一款专为Go语言开发者设计的集成开发环境(IDE)。它提供了全面的工具和服务,旨在提高Go语言开发的效率和生产力。→→↓↓载GoLand2023mac/win版 GoLand2023具有许多先进的特性,其中最突出的特点是其对Go语言的深度支持。它提供了智能代码编辑器,能够......
  • 无涯教程-Django - Apache配置
    到目前为止,在无涯教程的示例中,已经使用了DjangodevWeb服务器,但是此服务器仅用于测试,不适合生产环境。一旦投入生产,您就需要一个真实的服务器,如Apache,Nginx等,在本章中讨论Apache。通过Apache为Django应用提供服务是通过使用mod_wsgi完成的。因此,第一件事是确保您已安装Apache和......
  • 【Django开发】前后端分离美多商城项目:项目准备和搭建(附代码,文档)
    本系列文章md笔记(已分享)主要讨论django商城项目开发相关知识。本项目利用Django框架开发一套前后端不分离的商城项目(4.0版本)含代码和文档。功能包括前后端不分离,方便SEO。采用Django+Jinja2模板引擎+Vue.js实现前后端逻辑,Nginx服务器(反向代理)Nginx服务器(静态首页、商品详情页......
  • R语言非线性方程数值分析生物降解、植物生长数据:多项式、渐近回归、负指数方程、幂函
    全文链接:https://tecdat.cn/?p=33742原文出处:拓端数据部落公众号简介在选择最佳拟合实验数据的方程时,可能需要一些经验。当我们没有文献信息时该怎么办?我们建立模型的方法通常是经验主义的。也就是说,我们观察过程,绘制数据并注意到它们遵循一定的模式。例如,我们的客户可能观察......
  • CF1925D Good Trip 题解
    考虑分别计算\(p\)和\(q\)。按照期望的定义,\(q\)应该等于方案的总数,也就是\(s^k\),其中\(s\)表示一共有多少个不同的组。考虑如何求\(p\),我们先只计算第\(i\)组对\(p\)的贡献。如果第\(i\)组一共被选了\(1\)次,那么贡献为:\[g=f_i\timesC_{k}^{1}\times(s-1)^{......
  • django连接达梦
    问题1.django中连接数据库使用ORM框架,新增数据时,产生报错。问题描述:DatabaseError:[CODE:-2111]第1行附近出现错误:无效的列名[AAAAAAAAAAAAAAAAAR]查看数据库发现数据已经录入,问题出现在返回新增结果时候。通过查找发现是django_dmpython中自带文件调用方法时候的原生语......
  • django打包(linux)
    1.新建uvicorn文件(1)安装uvicorn插件pipinstalluvicorn(2)新建main.py文件fromuvicorn.serverimportConfig,Serverfromserver.asgiimportapplicationconfig=Config(app=application,host="0.0.0.0",port=8000,loop="asyncio",log_level=&q......
  • django打包(win)
    注意:1.打包工程(windows)1.安装pyinstallerpipinstallpyinstaller2.制作.spec文件pyi-makespec-Dmanage.py运行成功后可在项目所在目录下发现一个spec(规范)文件3.修改.spec文件把settings.py中apps里面的东西复制到manage.spec里面4.开始打包pyinstallermanage.......
  • go-cache基于内存的键值存储缓存库
    Go的内存key:valuestore/cache(类似于Memcached)库,适用于单机应用程序。文档https://pkg.go.dev/github.com/patrickmn/go-cachehttps://github.com/patrickmn/go-cachehttps://patrickmn.com/projects/go-cache/安装gogetgithub.com/patrickmn/go-cache方法funcN......
  • Django - admin 表单编辑页面,增加自定义功能,前端上传视频到oss
    #背景:可以在admin编辑页面原有基础上,增加一些可定制的功能,如:在本地上传图片到oss,减少服务器的带宽压力,下面就以此为例。示例图:  一。models.py#video可以直接用字符串存储,因为最终里面只有有一串oss的视频路径classNews(models.Model):OSS_URL='https://xxxx......