Golang Channel 详细原理和使用技巧

1.简介

        Channel(一般简写为 chan) 管道提供了一种机制:它在两个并发执行的协程之间进行同步,并通过传递与该管道元素类型相符的值来进行通信,它是Golang在语言层面提供的goroutine间的通信方式.通过Channel在不同的 goroutine中交换数据,在goroutine之间发送和接收消息,并且可以通过Channel实现Go依赖的CSP的并发模型这种同步模式

        chan 可以理解为一个管道或者先进先出的队列,Golang并发的核心哲学是:不要通过共享内存来通信,而应该通过通信来共享内存,所以数据在不同协程中的传输都是通过拷贝的形式完成的,并且 channel 本身还可以支持有缓冲无缓冲的,通过 channel + timeout 实现并发协程之间的同步也是常见的一种使用姿势

2.channel结构体

简单说明:

  • buf是有缓冲的channel所特有的结构,用来存储缓存数据,是个循环链表
  • sendxrecvx用于记录buf这个循环链表中的发送或者接收的index
  • lock是个互斥锁
  • recvqsendq分别是接收(<-channel)或者发送(channel <- xxx)的goroutine抽象出来的结构体(sudog)的队列,是个双向链表
   type hchan struct {qcount   uint           // total data in the queue 当前队列里还剩余元素个数dataqsiz uint           // size of the circular queue 环形队列长度,即缓冲区的大小,即make(chan T,N) 中的Nbuf      unsafe.Pointer // points to an array of dataqsiz elements 环形队列指针elemsize uint16 //每个元素的大小closed   uint32 //标识当前通道是否处于关闭状态,创建通道后,该字段设置0,即打开通道;通道调用close将其设置为1,通道关闭elemtype *_type // element type 元素类型,用于数据传递过程中的赋值sendx    uint   // send index 环形缓冲区的状态字段,它只是缓冲区的当前索引-支持数组,它可以从中发送数据recvx    uint   // receive index 环形缓冲区的状态字段,它只是缓冲区当前索引-支持数组,它可以从中接受数据recvq    waitq  // list of recv waiters 等待读消息的goroutine队列sendq    waitq  // list of send waiters 等待写消息的goroutine队列// lock protects all fields in hchan, as well as several// fields in sudogs blocked on this channel.//// Do not change another G's status while holding this lock// (in particular, do not ready a G), as this can deadlock// with stack shrinking.lock mutex //互斥锁,为每个读写操作锁定通道,因为发送和接受必须是互斥操作}// sudog 代表goroutinetype waitq struct {first *sudoglast  *sudog}

 3.Channel 操作符和操作方式

通信操作符 <- 的箭头指示数据流向,箭头指向哪里,数据就流向哪里,它是一个二元操作符,可以支持任意类型,对于 channel 的操作只有4种方式:

  • 创建 channel (通过make()函数实现,包括无缓存 channel 和有缓存 channel);
  • 向 channel 中添加数据(channel<-data);
  • 从 channel 中读取数据(data<-channel);
    • data<-channel, 从 channel 中接收数据并赋值给 data
    •  <-channel,从 channel 中接收数据并丢弃
  • 关闭 channel(通过 close()函数实现)
    •  读取关闭后的无缓存通道,不管通道中是否有数据,返回值都为 0 和 false。
    •  读取关闭后的有缓存通道,将缓存数据读取完后,再读取返回值为 0 和 false。
    • 对于一个关闭的 channel,如果继续向 channel 发送数据,会引起 panic
    • channel 不能 close 两次,多次 close 会 panic

4.Channel 有无缓冲 & 同步、异步

channel 分为有缓冲 channel 和无缓冲 channel,两种 channel 的创建方法如下:

  •  var ch = make(chan int) //无缓冲 channel,等同于make(chan int ,0),是一个同步的 Channel
    • 无缓冲 channel 在读和写的过程中是都会阻塞,由于阻塞的存在,所以使用 channel 时特别注意使用方法,防止死锁和协程泄漏的产生。
    • 无缓冲 channel 的发送动作一直要到有一个接收者接收这个值才算完成,否则都是阻塞着的,也就是说,发送的数据需要被读取后,发送才会完成
    • 一般要配合 select + timeout 处理,然后再在这里添加超时时间
  •  var ch = make(chan int,10) //有缓冲channel,缓冲大小是10,是一个异步的Channel
    • 带缓存的 channel 实际上是一个阻塞队列。队列满时写协程会阻塞,队列空时读协程阻塞。
    • 有缓冲的时候,写操作是写完之后直接返回的。相对于不带缓存 channel,带缓存 channel 不易造成死锁。

5.Channel 各种操作导致阻塞和协程泄漏的场景

写操作,什么时候会被阻塞?

  • 向 nil 通道发送数据会被阻塞
  • 向无缓冲 channel 写数据,如果读协程没有准备好,会阻塞
    • 无缓冲 channel ,必须要有读有写,写了数据之后,必须要读出来,否则导致 channel 阻塞,从而使得协程阻塞而使得协程泄漏
    •  一个无缓冲 channel,如果每次来一个请求就开一个 go 协程往里面写数据,但是一直没有被读取,那么就会导致这个 chan 一直阻塞,使得写这个 chan 的 go 协程一直无法释放从而协程泄漏。
  • 向有缓冲 channel 写数据,如果缓冲已满,会阻塞
    • 有缓冲的 channel,在缓冲 buffer 之内,不读取也不会导致阻塞,当然也就不会使得协程泄漏,但是如果写数据超过了 buffer 还没有读取,那么继续写的时候就会阻塞了。如果往有缓冲的 channel 写了数据但是一直没有读取就直接退出协程的话,一样会导致 channel 阻塞,从而使得协程阻塞并泄漏。

读操作,什么时候会被阻塞?

  •  从 nil 通道接收数据会被阻塞
  • 从无缓冲 channel 读数据,如果写协程没有准备好,会阻塞
  • 从有缓冲 channel 读数据,如果缓冲为空,会阻塞

close 操作,什么时候会被阻塞?

  • close channel 对 channel 阻塞是没有任何效果的,写了数据但是不读,直接 close,还是会阻塞的。

6.Channel 各种操作对应的状态

  • 正常的 channel,可读、可写
  • nil 的 channel,表示未初始化的状态,只进行了声明,或者手动赋值为 nil
  • 已经 closed 的 channel,表示已经 close 关闭了,千万不要误认为关闭 channel 后,channel 的值是 nil

7.Channel 长度和容量

容量(capacity)代表 Channel 容纳的最多的元素的数量,代表Channel的缓存的大小。如果没有设置容量,或者容量设置为0, 说明 Channel 没有缓存,长度和容量的两个函数是 cap 和 len 。

示例如下:

c := make(chan int, 100) // cap 就是 100,但是此时 len 为 0
c <- 0  // len = 1, cap = 100
c <- 0  // len = 2, cap = 100
<- c    // len = 1, cap = 100

 8.Channel 的缺点

Channel 的缺点:

  • Channel 可能会导致循环阻塞或者协程泄漏,这个是最最最要重点关注的
  • Channel 中传递指针会导致数据竞态问题(data race/ race conditions)
  • Channel 中传递的都是数据的拷贝,可能会影响性能,但是就目前我们的机器性能来看,这点数据拷贝所带来的 CPU 消耗,大多数的情况下可以忽略

9.Go Channel 实现协程同步

channel 实现并发同步的说明:channel 作为 Go 并发模型的核心思想:不要通过共享内存来通信,而应该通过通信来共享内存,那么在 Go 里面,当然也可以很方便通过 channel 来实现协程的并发和同步了,并且 channel 本身还可以支持有缓冲和无缓冲的,通过 channel + timeout 实现并发协程之间的同步也是常见的一种使用姿势。

无缓冲 chan 示例

示例如下:

package main
import "fmt"
func main() {var ch = make(chan string)for i := 0; i < 10; i++ {go sum(i, i+10, ch)}for i := 0; i < 10; i++ {fmt.Print(<-ch)}
}
func sum(start, end int, ch chan string) {var sum int = 0for i := start; i < end; i++ {sum += i}ch <- fmt.Sprintf("Sum from %d to %d is %d\n", start, end, sum)
}

有缓冲 chan 示例

    message_chan := make(chan int, 2)go func() {time.Sleep(time.Second * 3)println("start recv...")println(<-message_chan)println(<-message_chan)println(<-message_chan)println("finish recv...")}()println("start send 10...")message_chan <- 10println("start send 20...")message_chan <- 20println("start send 30...")message_chan <- 30println("finish send...")time.Sleep(time.Second * 3)close(message_chan)

参考

[go学习笔记.第十四章.协程和管道] 2.管道

Golang Channel 详细原理和使用技巧 - 知乎

Channels in Go(https://go101.org/article/channel.html)

How to Gracefully Close Channels(https://go101.org/article/channel-closing.html)

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

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

相关文章

代码随想录Day66 | 图的DFS与BFS

代码随想录Day66 | 图的DFS与BFS DFS797.所有可能的路径无向图和有向图的处理 BFS200.岛屿数量 DFS 文档讲解&#xff1a;代码随想录 视频讲解&#xff1a; 状态 本质上就是回溯算法。 void dfs(参数) {if (终止条件) {存放结果;return;}for (选择&#xff1a;本节点所连接的…

『运维备忘录』之 Shell 内置命令大集合

前言 在 Shell 中&#xff0c;有许多内置命令可用于执行各种任务&#xff0c;包括文件操作、进程管理、环境变量设置等。本文将详细介绍一些常见的Shell内置命令及其示例用法。 命令描述alias创建命令别名&#xff0c;用于将命令或命令组合关联到用户自定义名称bg将作业放入后…

Qt textBrowser的Html相关

Qt textBrowser的Html相关 Qt textBrowser的Html相关 Qt textBrowser的Html相关 一开始就想要一个简单的功能&#xff0c;点一下按钮&#xff0c;添加的文字居中显示&#xff0c;再点一下按钮&#xff0c;添加的文字变更颜色居右显示。 但是&#xff1a; ui->textEdit-&g…

WordPress免费的远程图片本地化下载插件nicen-localize-image

nicen-localize-image&#xff08;可在wordpress插件市场搜索下载&#xff09;&#xff0c;是一款用于本地化文章外部图片的插件&#xff0c;支持如下功能&#xff1a; 文章发布前通过编辑器插件本地化 文章手动发布时自动本地化 文章定时发布时自动本地化 针对已发布的文章…

python类的属性、方法、静态方法、静态方法类内部的调用、直接调用与实例化调用

设计者&#xff1a;ISDF工软未来 版本&#xff1a;v1.0 日期&#xff1a;2024/3/4 class Restaurant:餐馆类def __init__(self,restaurant_name,cuisine_type):#类的属性self.restaurant_name restaurant_nameself.cuisine_type cuisine_type# self.stregth_level 0def desc…

基于机器学习的密码强度检测

项目简介 利用机器学习对提供的数据集预测用户输入的密码是否为弱密码。 原始数据集只包含关于弱密码的信息&#xff0c;并没有包含强密码的数据或分类器&#xff0c;这意味着模型无法学习到强密码的规律!!! 我之所以这样设计这个示例&#xff0c;其目的是为了向你展示模型的…

python65-Python的循环之for表达式

for表达式用于利用其他区间、元组、列表等可迭代对象创建新的列表。for 表达式的语法格式如下: [表达式 for 循环计数器 in 可迭代对象] 从上面的语法格式可以看出,for表达式与普通for循环的区别 1)在for关键字之前定义一个表达式,该表达式通常会包含循环计数器 2)for …

@RestController和@Controller的区别

RestController和Controller是Spring框架中两个常用的注解&#xff0c;用于标识Controller类 1.Controller&#xff1a;使用Controller注解标记的类是一个典型的MVC控制器&#xff08;controller&#xff09;&#xff0c;用于处理请求并返回视图。通常在该类的方法上使用Reque…

alzet供应商你值得拥有

在20世纪70年代&#xff0c;ALZE公司研发出来一款巧妙的药物输送装置——Alzet osmotic pump。这款产品如胶囊般精致小巧&#xff0c;它既有胶囊的外表&#xff0c;也具有胶囊的作用。在Alzet osmotic pump中藏有可以装配药物溶液的空间。此款胶囊泵如同一个小投递员&#xff0…

Vue整合three.js 环境从头搭建 详细教程

目录 一、创建Vue项目 二、搭建three.js 三、引入扩展库OrbitControls 首先我们要有nodejs环境,这里我们用的是17.1.0版本。 一、创建Vue项目 第一步:终端命令创建vue项目 npm create vite@latest

AI蠕虫病毒威胁升级,揭示AI安全新危机

一组研究人员成功研发出首个能够通过电子邮件客户端窃取数据、传播恶意软件以及向他人发送垃圾邮件的AI蠕虫&#xff0c;并在使用流行的大规模语言模型&#xff08;LLMs&#xff09;的测试环境中展示了其按设计功能运作的能力。基于他们的研究成果&#xff0c;研究人员向生成式…

用pyinstaller打包python代码为exe可执行文件并在其他电脑运行的方法

本文介绍基于Python语言中的pyinstaller模块&#xff0c;将写好的.py格式的Python代码及其所用到的所有第三方库打包&#xff0c;生成.exe格式的可执行文件&#xff0c;从而方便地在其他环境、其他电脑中直接执行这一可执行文件的方法。 有时&#xff0c;我们希望将自己电脑上的…

python笔记:super(),__getitem()__,__call()__

目录 Q1&#xff1a;super(NetworkBlock, self).__init__()什么意思 Q2&#xff1a;__call__() 类的实例圆括号自动调用 Q3&#xff1a;__getitem__() 类的实例 方括号自动调用 Q4&#xff1a;没有双下划线方法时&#xff0c;普通的调用类的方法 总结 Q1&#xff1a;super…

Python 全栈系列230 轻全局函数服务 GFGoLite

说明 为了将GlobalFunc推向正常的应用轨道&#xff0c;构造一个功能简单的服务。 内容 1 工作模式 Server模式&#xff1a;以API服务方式执行 Worker模式: 以Worker方式执行。通过left/right文件夹和rsync方式执行任务并写结果。 2 构造方法 重载和执行&#xff1b;从标…

基于springboot的助农管理系统的设计与实现

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一 、设计说明 1.1研究背…

【网站项目】219一中体育馆管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

pywin32,一个超强的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超强的 Python 库 - pywin32。 Github地址&#xff1a;https://github.com/mhammond/pywin32 在Python的世界里&#xff0c;有许多优秀的第三方库可以帮助开发者更轻松地处理各种任务。其中&a…

【性能】后台与黑屏

目录 现象观察 调整应用的电池策略 现象观察 切换到后台&#xff0c; 一个心跳期间&#xff0c;就会发close socket 直接黑屏&#xff0c;没有收到任何消息&#xff0c;直接到onclose,然后有离线触发 也到时间。 调整应用的电池策略 修改成“无限制”后&#xff0c;就不会断…

C#面:简单介绍 序列化 和 反序列化

序列化&#xff1a; 是将对象转换为可保持或可传输的形式的过程。 .NET 具有以下序列化技术&#xff1a; 二进制序列化保持类型保真&#xff0c;这对于多次调用应用程序时保持对象状态非常有用。 例如&#xff0c;通过将对象序列化到剪贴板&#xff0c;可在不同的应用程序之…

Linxu自动化构建工具make/Makefile究竟时什么?

Linxu自动化构建工具make/Makefile究竟时什么&#xff1f; 一、简介二、makefile文件制作&#xff08;简洁版&#xff09;2.1 源文件2.2 makefile如何制作2.2.1 依赖关系、依赖方法2.2.3 伪目标&#xff08;清理文件资源&#xff09; 三、make/Makefile自动化原理3.1 伪目标为什…