Go 协程通道使用注意

目录

关闭channel

引入

 不关闭通道是有风险的,主要存在两条:

如何优雅的关闭?

1.只有一个发送者

2.多个发送者

判断通道是否关闭

有缓存和无缓存的通道有什么区别?

错误的关闭通道


关闭channel

close(chan):关闭通道并不会丢失里面的数据,只是让读取通道数据的时候不会读完之后一直阻塞等待新数据写入。

引入

看一看下面的例子:

ch := make(chan int, 1)
ch <- 11
// close(ch) // 如果不关闭,下面会引发死锁// 通道没有关闭的时候会阻塞,程序会永远等待从通道中接收值。而且由于没有其他goroutine来关闭通道,这个阻塞状态将会持续下去,从而引发死锁。
for v := range ch {fmt.Println(v)
}

 不关闭通道是有风险的,主要存在两条:

  1. 出现死锁:如果通道不会再被使用但未关闭,接收者可能会一直等待数据,导致死锁或无意义的等待。
  2. 出现资源泄漏:未关闭的通道可能会导致资源泄漏,因为垃圾收集器不会回收未关闭的通道。而且还要消耗电脑的性能来维护这条管道。

如何优雅的关闭?

在关闭管道的时候,我们要注意:正确地决定何时关闭通道、确保通道不会被多次关闭。(多次关闭同一个通道会报panic)

1.只有一个发送者

如果只有一个发送者,那么这个发送者在完成所有发送操作后可以直接关闭通道:

package mainimport ("fmt""time"
)// 边入边出
func main() {c := make(chan int, 5)go fibonacci(10, c)for v := range c {fmt.Println("out:", time.Now())fmt.Println(v)}
}// 应该由发送数据的一方关闭通道,当数据发送完毕后就是关闭通道的时候。
func fibonacci(n int, c chan int) {x, y := 0, 1for i := 0; i < n; i++ {c <- xfmt.Println("in:", time.Now())time.Sleep(100)x, y = y, x+y}close(c)
}// 大致运行结果:
// in: 2024-07-16 16:00:34.637655 +0800 CST m=+0.000171297
// out: 2024-07-16 16:00:34.637681 +0800 CST m=+0.000197020
// in: 2024-07-16 16:00:34.637952 +0800 CST m=+0.000467725
// 0
// in: 2024-07-16 16:00:34.637963 +0800 CST m=+0.000478508
// out: 2024-07-16 16:00:34.637968 +0800 CST m=+0.000483915
// 1
// in: 2024-07-16 16:00:34.637976 +0800 CST m=+0.000491801
// in: 2024-07-16 16:00:34.637983 +0800 CST m=+0.000499051
// out: 2024-07-16 16:00:34.637978 +0800 CST m=+0.000494157
// 1
// out: 2024-07-16 16:00:34.637999 +0800 CST m=+0.000514485
// 2
// out: 2024-07-16 16:00:34.638006 +0800 CST m=+0.000522241
// 3
// out: 2024-07-16 16:00:34.638013 +0800 CST m=+0.000528591
// 5
// in: 2024-07-16 16:00:34.637988 +0800 CST m=+0.000504017
// in: 2024-07-16 16:00:34.638039 +0800 CST m=+0.000555021
// in: 2024-07-16 16:00:34.638045 +0800 CST m=+0.000561340
// in: 2024-07-16 16:00:34.638049 +0800 CST m=+0.000564725
// in: 2024-07-16 16:00:34.638055 +0800 CST m=+0.000570642
// out: 2024-07-16 16:00:34.638052 +0800 CST m=+0.000568007
// 8
// out: 2024-07-16 16:00:34.638095 +0800 CST m=+0.000610606
// 13
// out: 2024-07-16 16:00:34.638103 +0800 CST m=+0.000619251
// 21
// out: 2024-07-16 16:00:34.63811 +0800 CST m=+0.000626188
// 34

2.多个发送者

当有多个发送者时,可以使用 sync.WaitGroup 来协调这些发送者,并在所有发送者完成后由一个协程关闭通道:

package mainimport ("fmt""sync"
)func main() {ch := make(chan string)var wg sync.WaitGroup// 启动3个协程发送数据for i := 1; i <= 5; i++ {wg.Add(1)go func(num int) {defer wg.Done()for j := 'a'; j <= 'e'; j++ {ch <- fmt.Sprintf("协程%d:%c", num, j)}}(i)}// 启动一个协程来等待所有发送者完成并关闭通道go func() {wg.Wait()close(ch)}()// 接收数据for val := range ch {fmt.Println(val)}fmt.Println("通道已关闭,结束接收")
}

判断通道是否关闭

v, ok := <-ch

如果 ok 为 true,表示成功从 channel 中接收到一个值,并且 channel 还没有关闭。如果 ok 为 false,表示 channel 已经关闭。

当一个 channel 已经关闭而且其中的元素已经全部被取出时,再从管道中取出数据会返回该元素类型的零值,并且 ok 会被设置为 false。这样的检查是为了防止在已关闭的 channel 上进行接收操作时,引发 panic。因为在已关闭的 channel 上进行接收操作会立即返回零值,但如果不进行检查,可能会误认为是从 channel 中接收到了有效的数据。因此,通过检查 ok,我们可以确定是否成功接收到了有效的值。

    c := make(chan int, 5)c <- 1c <- 2c <- 3close(c)v1, ok := <-cfmt.Println(v1, ok) // 1 truev1, ok = <-cfmt.Println(v1, ok) // 2 truev1, ok = <-cfmt.Println(v1, ok) // 3 truev1, ok = <-cfmt.Println(v1, ok) // 0 falsev1, ok = <-cfmt.Println(v1, ok) // 0 false

有缓存和无缓存的通道有什么区别?

参考下面链接:

golang channel有无缓冲区的区别

错误的关闭通道

错误一:多次关闭同一个通道

    c := make(chan int, 5)for i := 0; i < 5; i++ {defer close(c) // 报panic:同一个通道关闭了多次}

错误二:资源泄漏

    for i := 0; i < 5; i++ {c := make(chan int, 5)defer close(c)}

看起来没有错误,运行出来也没有错误。但是可能会导致资源泄漏,因为每次循环迭代中创建的通道不会立即关闭,导致内存和其他资源的泄漏。这五个管道的关闭时间都是在:所在函数运行完毕的时候关闭的。而不是本次循环结束后就立刻关闭。

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

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

相关文章

[HCTF 2018]WarmUp1

进入靶场&#xff0c;检查代码看到有source.php,访问 /source.php 读代码&#xff0c;在参数中传入 file&#xff0c;通过checkFile后&#xff0c;会加载file界面。 再看checkFile&#xff0c; 第一个判断&#xff0c;是非空并且是个字符串&#xff0c;否则返回false 第二个判…

LeetCode题练习与总结:最大间距--164

一、题目描述 给定一个无序的数组 nums&#xff0c;返回 数组在排序之后&#xff0c;相邻元素之间最大的差值 。如果数组元素个数小于 2&#xff0c;则返回 0 。 您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。 示例 1: 输入: nums [3,6,9,1] 输出: 3 …

微软研发致胜策略 01:尊定基础

这是一本老书&#xff0c;作者 Steve Maguire 在微软工作期间写了这本书&#xff0c;英文版于 1994 年发布。我们看到的标题是中译版名字&#xff0c;英文版的名字是《Debugging the Development Process》&#xff0c;这本书详细阐述了软件开发过程中的常见问题及其解决方案&a…

Docker安装Zookeeper、RocketMQ

安装Zookeeper 拉取镜像 docker pull zookeeper:3.9.2启动容器 -d后台启动&#xff0c;-p映射容器2181端口到宿主机2181端口&#xff0c;限制容器最大内存占用为128m&#xff0c;–restart容器自动重启 docker run -d -p 2181:2181 -m 128m --restartalways --name zookeepe…

特征映射(机器学习)

有时数据的分类并不像我们想象的那么简单&#xff0c;需要高次曲线才能分类。 就像下面的数据&#xff1a; 数据集最后给出&#xff1a; 我们这样看&#xff0c;至少需要达到2次以及以上的曲线才可以进行比较准确的分类。 比如如果已知数据有3列(两列特征) x1x2y-1-110.50.…

Axolotl

文章目录 一、关于 Axolotl特点Axolotl支持 二、快速入门⚡用法 三、环境设置1、Docker2、Conda/Pip venv3、Cloud GPU4、Bare Metal Cloud GPULambdaLabsGCP 5、Windows6、Mac7、Google Colab8、通过SkyPilot在公共云上启动9、通过 dstack 在公共云上启动 四、其他高级设置1、…

网站成长时间轴页面,网站发展记录页源码

一、源码描述 这是一款网站时间轴HTML源码&#xff0c;样式设计精美并且使用简单&#xff0c;主要用于记录你的网站发展历程&#xff0c;或者可以用于发布心情动态等&#xff0c;左侧年份可以折叠起来&#xff0c;页面底部是导航区域&#xff0c;可以自定义文本和链接。 二、…

Azure Repos 仓库管理

从远端仓库克隆到本地 前提:本地要安装git,并且登录了账户 1.在要放这个远程仓库的路径下,打git 然后 git clone https://.. 如果要登录验证,那就验证下 克隆完后,cd 到克隆的路径, 可以用 git branch -a //查看分支名 git status //查看代码状态 删除…

Shell基础之流程循环控制语句

目录 for循环 带列表循环 不带列表循环 类C风格循环 while循环 语法 无限循环 使用示例 until循环 基本语法 示例 select循环 语法格式 嵌套循环 break和continue break的使用 continue的使用 在shell 中循环有以下几种&#xff1a; for 循环 while …

对于GPT-5在一年半后发布的期待!

首先&#xff0c;如果GPT-5真如OpenAI首席技术官米拉穆拉蒂&#xff08;Mira Murati&#xff09;在采访中所透露的那样&#xff0c;在一年半后发布&#xff0c;并在某些领域达到博士级的智能&#xff0c;这无疑将是一个令人振奋的消息。这一预测不仅反映了AI技术的快速发展&…

MPAS跨尺度、可变分辨率模式实践技术

跨尺度预测模式&#xff08;The Model for Prediction Across Scales - MPAS&#xff09;是由洛斯阿拉莫斯实验室和美国国家大气研究中心(NCAR)共同开发&#xff0c;其由3个部分组成&#xff0c;分别称为 MPAS-A&#xff08;大气模型&#xff09;、MPAS-O&#xff08;海洋模型&…

python实例练习00001:打开文件输出文件内容

try:file input(enter the file :)with open(file, r) as f:data f.read()print(data) except FileNotFoundError:print(fthe file {file} does not exists:) 打开windows的cmd运行程序&#xff0c;效果如下&#xff1a;

MyBatis-Plus字段类型处理器介绍以及使用

文章目录 前言自定义 TypeHandler1. 创建 TypeHandler 类2. 注册自定义 TypeHandler2.1. 在 MyBatis XML 配置文件中注册&#xff1a;2.2. 在 Spring Boot 中注册&#xff1a;2.3. 在 Spring Boot配置文件注册&#xff1a; 3. 使用 TypeHandler3.1 在实体类属性上使用3.2 在 XM…

vscode常用组件

1.vue-helper 启用后点击右下角注册&#xff0c;可以通过vue组件点击到源码里面 2.【Auto Close Tag】和【Auto Rename Tag】 3.setting---Auto Reveal Exclude vscode跳转node_modules下文件&#xff0c;没有切换定位到左侧菜单目录> 打开VSCode的setting配置&#xff…

非法闯入智能监测摄像机:安全守护的新利器

在当今社会&#xff0c;安全问题愈发受到重视。随着科技的进步&#xff0c;非法闯入智能监测摄像机应运而生&#xff0c;成为保护家庭和财产安全的重要工具。这种摄像机不仅具备监控功能&#xff0c;还集成了智能识别和报警系统&#xff0c;能够在第一时间内检测到潜在的入侵行…

xxs攻击的攻击和防范

随着互联网技术的飞速发展&#xff0c;网络安全问题日益凸显。跨站脚本攻击&#xff08;XSS 攻击&#xff09;作为一种常见的网络攻击手段&#xff0c;给网络应用和用户数据安全带来了严重威胁。本文深入探讨了 XSS 攻击的原理、分类、危害以及相应的防范措施&#xff0c;旨在提…

第七天 SpringBoot与SpringCloud微服务项目交付

Spring Cloud微服务项目交付 微服务扫盲篇 微服务并没有一个官方的定义&#xff0c;想要直接描述微服务比较困难&#xff0c;我们可以通过对比传统WEB应用&#xff0c;来理解什么是微服务。 单体应用架构 如下是传统打车软件架构图&#xff1a; 这种单体应用比较适合于小项…

图像处理中的Scharr算子的原理,并附OpenCV和MATLAB示例代码

Scharr算子是图像处理中的一种边缘检测算子&#xff0c;主要用于计算图像梯度的边缘检测。与Sobel算子类似&#xff0c;Scharr算子也使用卷积核来计算图像的导数&#xff0c;但Scharr算子在精度和抗噪性方面表现更优。其原理如下&#xff1a; 原理 梯度计算 在图像处理中&…

服务器数据恢复—raid5阵列热备盘同步失败导致lun不可用的数据恢复案例

服务器存储数据恢复环境&#xff1a; 华为S5300存储中有一组由16块FC硬盘组建的RAID5磁盘阵列&#xff08;包含一块热备盘&#xff09;。 服务器存储故障&#xff1a; 该存储中的RAID5阵列1块硬盘由于未知原因离线&#xff0c;热备盘上线并开始同步数据&#xff0c;数据同步到…

Android C++系列:Linux文件系统(二)

1. VFS虚拟文件系统 Linux支持各种各样的文件系统格式&#xff0c;如ext2、ext3、reiserfs、FAT、NTFS、iso9660 等等&#xff0c;不同的磁盘分区、光盘或其它存储设备都有不同的文件系统格式&#xff0c;然而这些文件系统 都可以mount到某个目录下&#xff0c;使我们看到一个…