【从零单排Golang】第十六话:channel的用法和基本原则

在基于Golang的后端开发中,channel是一个必须要掌握的并发编程概念。和python的queue一样,channel在不同的goroutine里承担着传递信息的作用,使得业务逻辑的状态上下文可以在不同的goroutine中共享。今天,我们就来看一下channel的用法还有一些使用上的基本原则。

首先,我们需要知道什么场景下会用到channel。一个简单的例子是,在主流程里,我们希望启动一个方便处理panic的goroutine,异步跑一个任务,然后主流程等待这个goroutine给join进来。解决这个问题,就可以用到channel,代码这样写:

func TestAsyncTask(t *testing.T) {joiner := make(chan struct{})log.Printf("[main] start async task...")go func() {defer func() {if r := recover(); r != nil {log.Printf("[goroutine] panic: %v", r)}close(joiner)}()log.Printf("[goroutine] start async task...")time.Sleep(5 * time.Second) // task logiclog.Printf("[goroutine] end async task!")}()log.Printf("[main] wait for join...")<-joinerlog.Printf("[main] async task joined!")
}

我们定义一个channel,发起异步任务,并在主流程阻塞地去接收这个channel的事件。在异步任务执行完成后,把channel给close掉,这样主流程可以接收到channel给close掉的事件,就能继续后面的逻辑。这样,就达到了任务线程join的效果。

在上述的场景下,我们用到的channel相对于消息发送方是阻塞的,如果发送方给这个channel发送一条消息,而接收方的逻辑还没有跑到的话,那么发送方就会一直阻塞在发送逻辑。假使我们需要实现一个类dispatcher(多consumer)的调度模型,不断地去根据消息内容把消息分发到不同consumer-worker上,那么采用对于发送方阻塞的channel的话,就有可能因为dispatcher在处理过程中产生瓶颈,造成发送方等待超时。在这种情况下,我们就需要用到非阻塞的channel,也就是在定义channel时,也去声明这个channel的buffer大小,这样我们就可以有足够的缓冲区去缓存消息,解决发送方的无限等待问题。

// channel chan instance
var channel chan int// bufSize for non-blocking channel
const bufSize = 1024func initBlockingChannel() {channel = make(chan int)
}func initNonBlockingChannel() {channel = make(chan int, bufSize)
}func exampleProduceMsg() {msg := 1select {case channel <- msg:log.Printf("sent msg: %v", msg)default:log.Printf("send msg failed!")}
}

而消费一个channel,我们可以构建一个loop,来循环处理channel发送过来的内容。比如一个dispatcher,就需要一个for循环,不断拿消息,把消息发给下游的任务handler:

func launchConsumer(c <-chan int) {numMsgs := 0defer func() {log.Printf("[Consumer] overall received %d msgs!", numMsgs)}()for { // 其它写法:for msg := range cselect {case msg, ok := <-c:if ok { // 收到了消息log.Printf("[Consumer] received msg: %v", msg)numMsgs++} else { // channel closedlog.Printf("[Consumer] channel closed!")return}}}
}

channel使用的基本原则是,从producer端去close掉channel。produce端触发close后,consumer端就能够知道channel被close掉,进而结束掉自己的chunk。而如果是consumer端主动close,producer端在不知情的情况下,往channel发送消息,就会panic。

因此,为了规避这个风险,一是要从producer去关channel,而是不论是怎样的生产消费模型,都需要保证channel仅被close一次。简单来讲,close掉channel的操作,放到producer最外层函数的defer里面,就能解决问题。

对于单个producer的模拟,我们可以简单做一个for循环,去不断发送消息。中途打断的方式采用可cancel的context,当循环过程中检测到context被cancel掉,就停止发送消息。整个代码如下:

func launchSingleProducer(c chan<- int) {defer func() {log.Printf("[SingleProducer] close channel...")close(channel)}()numMsgs := 10for i := 0; i < numMsgs; i++ {log.Printf("[SingleProducer] start send msg: %v", i)select {case c <- i:log.Printf("[SingleProducer] finish send msg: %v", i)time.Sleep(1 * time.Second)case <-ctx.Done():log.Printf("[SingleProducer] context done!")returndefault:log.Printf("[SingleProducer] send msg failed...")time.Sleep(1 * time.Second)}}
}

golang中ctx的上下文信息,也可以在不同的goroutine中共享,可以参考这篇文章对于context模块的介绍。在单个producer场景下,每次循环,我们可以select不同的信道,看当刻是可以给channel发送一条消息,还是收到了ctx上下文结束的事件。如果上下文结束掉,就终止整个producer。close操作,放到defer里执行即可。

consumer端处理多个channel的发送/接收事件,我们可以通过循环+select的通用模版去实现。每轮循环,就select单个channel的单个事件来处理,代码写起来也简洁明了。

对于多个producer的模拟,我们可以创建一个waitGroup去管理多个producer的进度,可以参考这篇文章来了解waitGroup的用法。与此同时,每个producer的行为则是在for循环里发消息,直到消息发完或者context结束掉,才结束单个producer的进度。在主goroutine中,只需要wait这个waitGroup,然后在defer中close掉channel即可。代码如下:

func launchMultiProducers(c chan<- int) {defer func() {log.Printf("[SingleProducer] close channel...")close(channel)}()produce := func(id int, numMsgs int) {for i := 0; i < numMsgs; i++ {msg := id*10000 + ilog.Printf("[MultiProducers] [%d] start send msg: %v", id, msg)select {case c <- i:log.Printf("[MultiProducers] [%d] finish send msg: %v", id, msg)time.Sleep(1 * time.Second)case <-ctx.Done():log.Printf("[MultiProducers] [%d] context done, break!", id)returndefault:log.Printf("[SingleProducer] send msg failed...")time.Sleep(1 * time.Second)}}log.Printf("[MultiProducers] [%d] finish send all msgs!", id)}numIDs := 10numMsgsEach := 10waitGroup := sync.WaitGroup{}log.Printf("[MultiProducers] launch producers...")for x := 1; x <= numIDs; x++ {waitGroup.Add(1)id := xgo func() {defer waitGroup.Done()produce(id, numMsgsEach)}()}waitGroup.Wait()log.Printf("[MultiProducers] finish all producers!")
}

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

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

相关文章

C语言隐藏执行其他程序

WinExec函数 #include<windows.h> int main() {WinExec("notepad.exe", SW_HIDE);return 0; }SW_HIDE 隐藏SW_SHOW 显示 ShellExecute函数 在C语言中使用ShellExecute函数可以执行外部程序&#xff0c;比如打开一个文件、运行一个程序等。 #include <wi…

Singleton单例设计模式详解

目录 模式定义应用场景实现方式1.懒汉模式&#xff1a;2.饿汉模式&#xff1a;3.静态内部类反射如何防止反射攻击破坏&#xff1f; 枚举类型序列化 部分源码中的应用定位Spring & JDKTomcat反序列化指定数据源 模式定义 保证一个类只有一个实例&#xff0c;并且提供一个全…

pytorch车牌识别

目录 使用pytorch库中CNN模型进行图像识别收集数据集定义CNN模型卷积层池化层全连接层 CNN模型代码使用模型 使用pytorch库中CNN模型进行图像识别 收集数据集 可以去找开源的数据集或者自己手做一个 最终整合成 类别分类的图片文件 定义CNN模型 卷积层 功能&#xff1a;提…

Chatgpt掘金之旅—有爱AI商业实战篇|播客剧本写作|(十三)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、AI技术创业播客剧本写作服务有哪些机会&#xff1f; 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。…

b站江科大stm32笔记(持续更新)

b站江科大stm32笔记&#xff08;持续更新&#xff09; 片上资源/外设引脚定义表启动配置推挽开漏oc/od 门漏极/集电极 电阻的上拉下拉输入捕获输入捕获通道主从触发模式输入捕获基本结构PWMI基本结构PWMPSC ARR CRR输入捕获模式测频率TIM_PrescalerConfig()初始化输入捕获测频法…

mysql四种引擎区别

MySQL 提供了多种不同的数据库引擎&#xff0c;其中最常见的有 MyISAM、InnoDB、MEMORY 和 BLACKHOLE。这四个引擎分别有以下特点&#xff1a; 1. MyISAM MyISAM 是 MySQL 的默认引擎。它对于只有较少的修改、大量读取的应用场景具有良好的性能。它不支持事务处理&#xff0c;也…

Steam平台游戏发行流程

Steam平台游戏发行流程 大家好我是艾西&#xff0c;一个做服务器租用的网络架构师也是游戏热爱者&#xff0c;经常在steam平台玩各种游戏享受快乐生活。去年幻兽帕鲁以及雾锁王国在年底横空出世&#xff0c;幻兽帕鲁更是在短短一星期取得了非常好的成绩&#xff0c;那么作为游戏…

强制删除命名空间(K8S 强制删除卡在Terminating状态的namespaces 记录)

参考&#xff1a;https://blog.csdn.net/lxy___/article/details/106644302 调用API接口删除 [rootmaster ~]# netstat -ntlp | grep kube-apiserve tcp6 0 0 :::6443 :::* LISTEN 7737/kube-apiserver #API接口查询 […

你的系统是如何跟MySQL打交道的

1、Java 工程师眼中的数据库是什么东西? 从今天开始&#xff0c;我们将要开始一个MySQL的专栏&#xff0c;一起来研究MySQL数据库的底层原理和各种实践案例&#xff0c;以及互联网公司的技术方案。 现在我们先来看看&#xff0c;在一个Java工程师眼中的数据库是什么东西? 平时…

常见分词器tokenizer汇总

常见分词器tokenizer 大模型中的分词器&#xff1a;BPE、WordPiece、Unigram LM、SentencePiece Byte Pair Encoding (BPE) OpenAI 从GPT2开始分词就是使用的这种方式&#xff0c;BPE每一步都将最常见的一对相邻数据单位替换为该数据中没有出现过的一个新单位&#xff0c;反…

速盾:海外CDN加速专线

随着全球互联网的发展&#xff0c;网络访问速度成为了用户体验中非常重要的一个因素。特别是在访问海外网站或使用海外应用时&#xff0c;传统的网络连接往往会出现延迟或不稳定的情况。为了解决这个问题&#xff0c;CDN&#xff08;Content Delivery Network&#xff09;技术应…

【Godot4.2】CanvasItem绘图函数全解析 - 5.绘制字符和字符串

概述 到这一节为止&#xff0c;我们已经学习了如何在CanvasItem中绘制简单几何图形、图片以及样式盒。但是对于很重要的文字一直没有涉及。 本节就来讲一下字符和字符串绘制函数&#xff0c;以及替换它们的两个类。 系列目录 0.概述1.绘制简单图形2.设定绘图变换3.绘制纹理4…

解决vue3更新chunk包后,点击页面报错

出现错误 解决思路 试了好多方法&#xff0c;跳了很多坑&#xff0c;router版本对不上&#xff0c;解决方案不实用。最后我直接捕获异常&#xff0c;刷新页面&#xff0c;解决最快最有效。 // vue-rotuer版本 "vue-router": "^4.0.3"解决方案 在router/…

路由器配置实验--R1---R5

R1的路由表中默认存在:192.168.1.0192.168.3.0 需要添加:192.168.2.0 4.0 5.0 R2的路由表中默认存在:192.168.1.0192.168.2.0需要添加:192.168.3.0 4.0 5.0 R3的路由表中默认存在:192.168.3.0192.168.4.0需要添加: 1.0 2.0 5.0 R4的路由表中默认存在:192.168.2.0 192.168.4.0…

面试算法-164-K 个一组翻转链表

题目 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内…

深入微服务框架:构建高效、可扩展与弹性的现代应用架构

前言&#xff1a;当今快速迭代和多变的商业环境中&#xff0c;传统的单体应用程序面临着一系列挑战&#xff0c;包括难以管理复杂性、缺乏灵活性以及无法有效扩展等问题。随着业务需求的不断增长和技术栈的不断演进&#xff0c;企业亟需一种更加模块化、易于管理和扩展的应用程…

给你的AppImage创建桌面快捷方式

原文链接 https://www.cnblogs.com/HGNET/p/16396589.html 运行环境:Ubuntu 22.04 LTS 1.首先准备好AppImage文件并放在一个你知道的地方 2.打开终端&#xff0c;在/usr/share/applications下新建APP.desktop文件&#xff08;APP可以改成你的应用名称&#xff09; cd /usr/s…

Android 下载、显示图片

一、新建PictureLoader public class PictureLoader {private ImageView loadImg;private String imgUrl;private byte[] picByte;Handler handler new Handler() {Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (msg.what 0x123) {if (picB…

在 Elasticsearch 中扩展 ML 推理管道:如何避免问题并解决瓶颈

作者&#xff1a;来自 Elastic Iulia Feroli 是时候考虑语义搜索运营了吗&#xff1f; 无论你是一位经验丰富的搜索工程师&#xff0c;希望探索新的人工智能功能&#xff0c;还是一位机器学习专家&#xff0c;希望更多地利用搜索基础设施来增强语义相似性模型 —— 充分利用这…

易舟云财务软件免费版和专业版有什么区别?

文章目录 1、价格&#xff08;1&#xff09;免费版&#xff08;2&#xff09;专业版 2、版本功能&#xff08;1&#xff09;免费版&#xff08;2&#xff09;专业版 1、价格 &#xff08;1&#xff09;免费版 永久免费&#xff01; &#xff08;2&#xff09;专业版 298元/…