Golang并发模型:合理退出并发协程

goroutine作为Golang并发的核心,我们不仅要关注它们的创建和管理,当然还要关注如何合理的退出这些协程,不(合理)退出不然可能会造成阻塞、panic、程序行为异常、数据结果不正确等问题。这篇文章介绍,如何合理的退出goroutine,减少软件bug。

goroutine在退出方面,不像线程和进程,不能通过某种手段强制关闭它们,只能等待goroutine主动退出。但也无需为退出、关闭goroutine而烦恼,下面就介绍3种优雅退出goroutine的方法,只要采用这种最佳实践去设计,基本上就可以确保goroutine退出上不会有问题,尽情享用。

1:使用for-range退出

for-range是使用频率很高的结构,常用它来遍历数据,range能够感知channel的关闭,当channel被发送数据的协程关闭时,range就会结束,接着退出for循环。

它在并发中的使用场景是:当协程只从1个channel读取数据,然后进行处理,处理后协程退出。下面这个示例程序,当in通道被关闭时,协程可自动退出。

go func(in <-chan int) {// Using for-range to exit goroutine// range has the ability to detect the close/end of a channelfor x := range in {fmt.Printf("Process %d\n", x)}
}(inCh)

2:使用,ok退出

for-select也是使用频率很高的结构,select提供了多路复用的能力,所以for-select可以让函数具有持续多路处理多个channel的能力。但select没有感知channel的关闭,这引出了2个问题

  1. 继续在关闭的通道上读,会读到通道传输数据类型的零值,如果是指针类型,读到nil,继续处理还会产生nil。
  2. 继续在关闭的通道上写,将会panic。

问题2可以这样解决,通道只由发送方关闭,接收方不可关闭,即某个写通道只由使用该select的协程关闭,select中就不存在继续在关闭的通道上写数据的问题。

问题1可以使用,ok来检测通道的关闭,使用情况有2种。

第一种:如果某个通道关闭后,需要退出协程,直接return即可。示例代码中,该协程需要从in通道读数据,还需要定时打印已经处理的数量,有2件事要做,所有不能使用for-range,需要使用for-select,当in关闭时,ok=false,我们直接返回。

go func() {// in for-select using ok to exit goroutinefor {select {case x, ok := <-in:if !ok {return}fmt.Printf("Process %d\n", x)processedCnt++case <-t.C:fmt.Printf("Working, processedCnt = %d\n", processedCnt)}}
}()

第二种:如果某个通道关闭了,不再处理该通道,而是继续处理其他case,退出是等待所有的可读通道关闭。我们需要使用select的一个特征:select不会在nil的通道上进行等待。这种情况,把只读通道设置为nil即可解决。

go func() {// in for-select using ok to exit goroutinefor {select {case x, ok := <-in1:if !ok {in1 = nil}// Processcase y, ok := <-in2:if !ok {in2 = nil}// Processcase <-t.C:fmt.Printf("Working, processedCnt = %d\n", processedCnt)}// If both in channel are closed, goroutine exitif in1 == nil && in2 == nil {return}}
}()

3:使用退出通道退出

使用,ok来退出使用for-select协程,解决是当读入数据的通道关闭时,没数据读时程序的正常结束。想想下面这2种场景,,ok还能适用吗?

  1. 接收的协程要退出了,如果它直接退出,不告知发送协程,发送协程将阻塞。
  2. 启动了一个工作协程处理数据,如何通知它退出?

使用一个专门的通道,发送退出的信号,可以解决这类问题。以第2个场景为例,协程入参包含一个停止通道stopCh,当stopCh被关闭,case <-stopCh会执行,直接返回即可。

当我启动了100个worker时,只要main()执行关闭stopCh,每一个worker都会都到信号,进而关闭。如果main()向stopCh发送100个数据,这种就低效了。

func worker(stopCh <-chan struct{}) {go func() {defer fmt.Println("worker exit")// Using stop channel explicit exitfor {select {case <-stopCh:fmt.Println("Recv stop signal")returncase <-t.C:fmt.Println("Working .")}}}()return
}

最佳实践回顾

  1. 发送协程主动关闭通道,接收协程不关闭通道。技巧:把接收方的通道入参声明为只读,如果接收协程关闭只读协程,编译时就会报错。
  2. 协程处理1个通道,并且是读时,协程优先使用for-range,因为range可以关闭通道的关闭自动退出协程。
  3. ,ok可以处理多个读通道关闭,需要关闭当前使用for-select的协程。
  4. 显式关闭通道stopCh可以处理主动通知协程退出的场景。

完整示例代码

本文所有代码都在仓库,可查看完整示例代码:https://github.com/Shitaibin/...

并发系列文章推荐

  • Golang并发模型:轻松入门流水线模型
  • Golang并发模型:轻松入门流水线FAN模式
  • Golang并发模型:并发协程的优雅退出
  • Golang并发模型:轻松入门select
  1. 如果这篇文章对你有帮助,请点个赞/喜欢,鼓励我持续分享,感谢。
  2. 我的文章列表,点此可查看
  3. 如果喜欢本文,随意转载,但请保留此原文链接。

一起学Golang-分享有料的Go语言技术

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

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

相关文章

剑网服务器维护,12月31日服务器例行维护公告

隐元秘鉴新增以下江湖行里使用趣味道具的成就&#xff1a;压酒唤客尝&#xff1a;使用压酒三十次欲解红烛意&#xff1a;使用烛影三十次闲情吹笛子&#xff1a;使用吹断三十次引弦中落雀&#xff1a;使用千鸟三十次黄云动风色&#xff1a;使用风色三十次卷抒平生意&#xff1a;…

一款 Windows 软件快捷助手

WPF 开发的 Windows 软件快捷助手Windows 软件快捷助手作者&#xff1a;WPFDevelopersOrg - 驚鏵原文链接&#xff1a;https://github.com/WPFDevelopersOrg/SoftwareHelper框架使用.NET40&#xff1b;Visual Studio 2019;项目使用 MIT 开源许可协议&#xff1b;项目使用 MVV…

关于8位AD_DA转换芯片的采样率问题

关于使用Keil计算程序执行时间 打开Keil程序&#xff0c;进入“启动/停止调试”界面。在需要暂停的地方设置断点&#xff08;在该句程序前双击&#xff09;。在程序上方有一行工具栏&#xff1a;此工具栏分别代表复位、运行、停止、步进、步越、步出、运行到光标处等。 点击运…

CYQ.Data 数据框架 V4.0 开源版本发布(源码提供下载,秋色园V2.5版本标配框架)

说明的说明&#xff1a; 博客园团队两次移此文出首页&#xff0c;说 这篇文章不属于知识分享型文章&#xff0c;并且有广告嫌疑。 本文的确属于分享型文章&#xff0c;而且分享的知识点比其它文章都多很多&#xff0c;看看网友回复“谢谢分享”就知道是分享型文章了。 所谓广告…

oracle 分组后取每组第一条数据

数据格式 分组取第一条的效果 sql SELECT * FROM (SELECT ROW_NUMBER() OVER(PARTITION BY x ORDER BY y DESC) rn, test1.* FROM test1) WHERE rn 1 ;

永大服务器进去显示字母,永大电梯服务器使用说明

永大电梯服务器使用说明2021-05-25一&#xff0e; 目的&#xff1a;用于工务交车前对MPU和XDR板进行调试。二&#xff0e; 对应作业&#xff1a;1-1对MPU电梯调试接线连接&#xff1a;1).对MPUGB2(A2)版电梯调试时(以及A2前版本)&#xff0c;接线情况如下&#xff1a;连接顺序连…

树莓派Zero 2 W(ubuntu-22.04)通过.NET6和libusb操作USB读写

有这个想法的初衷喜欢电子和DIY硬件的朋友对稚晖君应该都不陌生&#xff0c;他定期都会分享一些自己做的好玩的硬件&#xff0c;他之前做了一个ElectronBot桌面机器人我就很感兴趣&#xff0c;所以就自己也做了一个。起初我只是自己开发了一个叫电子脑壳的上位机软件&#xff0…

bzoj4589

fwt 原理并不知道 nim游戏石子异或和0后手赢 那么也就是求a[1]^a[2]^...^a[n]0的方案数 这个和bzoj3992一样可以dp dp[i][j]表示前i个数异或和为j的方案数 dp[0][0] 1 dp[i][j] dp[i - 1][k] * a[p] p ^ k j a[p] 0 / 1 表示有没有p这个数 这个东西也不能矩阵快速幂 但是我…

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 85

UnicodeDecodeError: ascii codec cant decode byte 0xe5 in position 85;import sys reload(sys) sys.setdefaultencoding(utf8)

JS设计模式五:职责链模式

职责链模式简述 职责连是由多个不同的对象组成的&#xff0c;有发送者跟接收者&#xff0c;分别负责信息的发送跟接收&#xff0c;其中&#xff0c;链中第一个对象是 职责连是由多个不同的对象组成的&#xff0c;发送者是发送请求的对象&#xff0c;接收者接收请求并且对其进行…

web框架之Django(一)

Python的WEB框架有Django、Tornado、Flask 等多种&#xff0c;Django相较与其他WEB框架其优势为&#xff1a;大而全&#xff0c;框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能。 基本配置 一、创建django程序 终端命令&#xff1a;django-admin startprojec…

写一个易于维护使用方便性能可靠的Hybrid框架(一)—— 思路构建

写一个易于维护使用方便性能可靠的Hybrid框架&#xff08;二&#xff09;—— 插件化 写一个易于维护使用方便性能可靠的Hybrid框架&#xff08;三&#xff09;—— 配置插件 前言 本来上一篇博文写完&#xff0c;我就告诉自己&#xff0c;这是最后一篇&#xff0c;之后不再总结…

程序员制作出价值5亿外卖神器却不能取消订单,你知道吗?

小编今日给大家带来RACDisopsable&#xff0c;大家可能有部分人对这个会感觉到很陌生&#xff0c;那么我就用一句话来表达就是他可以帮我们取消订阅。那么又会有人会对这个产生疑问了&#xff0c;我们什么时候需要用到这个取消订阅了打个实际的例子来说吧&#xff0c;今天我在饿…

Computer

链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid2196https://blog.csdn.net/shuangde800/article/details/9732825#include<iostream> #include<cstdio> #include<algorithm> #include<vector> #include<queue> #include<cmath&…

智慧“昆明”在路上 未来充满精彩

智慧城市是运用物联网、云计算、大数据、移动互联网、空间地理信息集成等新一代信息技术&#xff0c;促进城市规划、建设、管理和服务智慧化的新理念和新模式。近年来&#xff0c;昆明市全面加快智慧城市建设&#xff0c;力争通过三年的努力&#xff0c;打造区域信息辐射中心的…

《精读 Mastering ABP Framework》教程发布

精读《Mastering ABP Framework》学习总结&#xff0c;掌握软件开发最佳实践&#xff0c;构建可维护 .NET 解决方案。从 ABP Framework 框架中学习如何构建现代 WEB 应用程序。掌握 ABP Framework 框架ABP Framework 是一个完整的基础架构&#xff0c;遵循软件开发最佳实践&…

C# 委托知识总结

1.什么是委托&#xff0c;为什么要使用委托 我正在埋头苦写程序&#xff0c;突然想喝水&#xff0c;但是又不想自己去掉杯水而打断自己的思路&#xff0c;于是我就想让女朋友去给我倒水。她去给我倒水&#xff0c;首先我得让她知道我想让她干什么&#xff0c;通知她之后我可以继…

阿里云大学课程学习有奖征文活动现在开始

2019独角兽企业重金招聘Python工程师标准>>> "学有所获&#xff0c;分享为美"--阿里云大学课程学习有奖征文活动开始啦~~ 看课程&#xff0c;写心得&#xff0c;赢千元大奖&#xff0c;还有机会加入阿里云大学技术作者群&#xff01;想试试自己的技术文笔…

配置网络测试环境的批处理

引言 有次需要测试 50 台左右的设备&#xff0c;每个都要连上电脑并搭好测试环境。这种事当然用服务器下发配置最方便&#xff0c;但条件不允许哦&#xff0c;只得手工一台台设。 写了个批处理配置脚本&#xff0c;放到 U 盘上&#xff0c;最好再配上 autorun.inf&#xff0c;嘿…

Android 的系统架构

Android 的系统架构和其它操作系统一样&#xff0c;采用了分层的架构。android 分为四个层&#xff0c;从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和 linux 核心层。 Android 是以 Linux 为核心的手机操作平台&#xff0c;作为一款开放式的操作系统&#xf…