当前位置: 首页 > news >正文

【golang】channel原理和机制

本文结合源码及channel使用场景对其原理进行解析

一、channel数据结构:

type hchan struct {qcount   uint           // total data in the queuedataqsiz uint           // size of the circular queuebuf      unsafe.Pointer // points to an array of dataqsiz elementselemsize uint16closed   uint32elemtype *_type // element typesendx    uint   // send indexrecvx    uint   // receive indexrecvq    waitq  // list of recv waiterssendq    waitq  // list of send waiterslock mutex
}

核心结构

1.环形数组,承载channel的缓冲功能。环形数组本质上是由 定长数组 + 头尾下标组成,之所以叫做环形数组,在于尾部指针触尾后可以移动到头部,剩余空间可以得到充分利用,同时数组具有天然的局部性原理

2.锁,channel支持并发操作,必然要对其buffer做线程安全的设计

3.读队列和写队列,两个等待队列,如果有操作该channel的协程因锁被占用而阻塞,将陷入阻塞的协程使用等待队列管理,便于后续唤醒。插个眼,这个特性将作为后续的使用场景解析的前置条件

二、有缓冲和无缓冲channel

有无缓冲是根据hchan对象的buf是否为空决定,若为无缓冲型,则仅申请一个大小为默认值 96 的空间

1.无缓冲

  • 发送操作会阻塞至另一个协程接受数据为止
  • 强同步,类似于信号量的功能

2.有缓冲

  • 发送操作只会在缓冲区满时阻塞
  • 弱同步,异步通信

三、初始化、未初始化channel

1.直接创建channel,未初始化,对应chan为nil

2.通过make创建会自动初始化

使用场景:

  • 阻塞模式下(非select),读/写一个未初始化的chan,会死锁,该协程不会被唤醒;非阻塞模式下返回error
  • 对于已经关闭的chan,写操作会painc
  • 对于已经关闭的chan,如果缓冲区有空间,正常读取并返回数据和true,如果缓冲区为空,返回零值(0或“”)和fasle

四、写操作场景(需加锁)

1.写操作时存在阻塞的读协程

读阻塞意味着写入前缓冲区为空,此时写协程会直接从阻塞队列里取出一个协程,直接memmove数据到对方,并唤醒该协程

2.写时无阻塞读协程且环形缓冲区无空间

此时会单纯因为缓冲区满而阻塞该写协程,将其加入到写阻塞队列中,主动park阻塞。直到有读协程因缓冲区空而去唤醒该阻塞的写协程

五、读操作场景(需加锁)

1.读时有阻塞的写协程

说明缓冲区满,写操作阻塞

如果channel无缓冲区,直接唤醒该写协程,读取其元素,也就是强同步的逻辑

如果channel有缓冲区,先读缓冲区,然后再唤醒一个写阻塞的协程(因为读后缓冲区有空间了)

2.读时无阻塞写协程且缓冲区无元素

也就是目前无法读到数据的情况,将该协程加入读阻塞队列,主动park陷入阻塞

六、阻塞与非阻塞模式

结论:除select外,默认channel 为阻塞模式

1.非阻塞模式逻辑区别

在select非阻塞模式下,读/写 channel 方法通过一个 bool 型的响应参数,用以标识是否读取/写入成功,实际并不阻塞,而是cas轮询

• 所有需要使得当前 goroutine 被挂起的操作,在非阻塞模式下都会返回 false;

• 所有是的当前 goroutine 会进入死锁的操作,在非阻塞模式下都会返回 false;

• 所有能立即完成读取/写入操作的条件下,非阻塞模式下会返回 true.

为什么select需要非阻塞?

select的初衷就是多路复用,同时监听多个事件,如果某一个分支因为:

1.读/写一个未初始化的chan,会死锁

2.读/写操作阻塞,导致整个select协程阻塞

所以非阻塞模式下,若某一个case未就绪,会直接返回并持续轮询

七、关闭channel

  • 关闭未初始化过的 channel 会 panic;
  • 重复关闭 channel 会 panic;
  • 唤醒 该channel的阻塞队列里的所有协程

相关文章:

  • Leetcode 159. 至多包含两个不同字符的最长子串
  • 金额高精度计算-BigDecimal
  • .NET WinForm图像识别二维码/条形码
  • Day39
  • 【Pandas】pandas DataFrame equals
  • 构筑电网“无形防线”: 防外破告警在线监测服务系统
  • 数据结构 -- 判断正误
  • 【数据结构】栈和队列(下)
  • 从零开始创建 Vue 3 开发环境并构建第一个 Demo
  • 【Pandas】pandas DataFrame duplicated
  • Opencv实用操作5 图像腐蚀膨胀
  • WPF log4net用法
  • Facebook 的隐私保护措施是否足够?技术观点
  • 1614. 括号的最大嵌套深度【 力扣(LeetCode) 】
  • LVS+KeepAlived
  • ansible template 文件中如果包含{{}} 等非ansible 变量处理
  • 【python深度学习】Day 39 图像数据与显存
  • 关于 JavaScript 版本、TypeScript、Vue 的区别说明, PHP 开发者入门 Vue 的具体方案
  • 2.spring基础入门(二)
  • 充电便捷,新能源汽车移动充电服务如何预约充电
  • 大气扁平网站/域名注册阿里云
  • 做普通网站价格/windows7系统优化工具
  • 网站建设装修/怎样优化网站关键词排名靠前
  • 网站的建设费用预算策划书/seo服务 文库
  • 深圳企业公司做网站/谷歌首页
  • 新网站建设验收/营销网站建设价格