向channel写入数据主要流程如下:
- CASE1:当channel为空或者未初始化,如果block表示阻塞那么向其中发送数据将会永久阻塞;如果block表示非阻塞就会直接return;
- CASE2:前置场景,block为非阻塞,且channel没有关闭(已关闭的channel不能写入数据)且(channel为非缓冲队列且receiver等待队列为空)或则( channel为有缓冲队列但是队列已满),这个时候直接return;
- 调用 lock(&c.lock) 锁住channel的全局锁;
- CASE3:不能向已经关闭的channel send数据,会导致panic 。
- CASE4:如果channel上的recv队列非空,则跳过channel的缓存队列,直接向消息发送给接收的goroutine:
- 调用sendDirect方法,将待写入的消息发送给接收的goroutine;
- 释放channel的全局锁;
- 调用goready函数,将接收消息的goroutine设置成就绪状态,等待调度 。
- CASE5:缓存队列未满,则将消息复制到缓存队列上,然后释放全局锁;
- CASE6:缓存队列已满且接收消息队列recv为空,则将当前的goroutine加入到send队列;
- 获取当前goroutine的sudog,然后入channel的send队列;
- 将当前goroutine休眠
func closechan(c *hchan) {if c == nil {panic(plainError("close of nil channel"))}lock(&c.lock)if c.closed != 0 {unlock(&c.lock)panic(plainError("close of closed channel"))}if raceenabled {callerpc := getcallerpc()racewritepc(c.raceaddr(), callerpc, funcPC(closechan))racerelease(c.raceaddr())}c.closed = 1var glist gList// release all readersfor {sg := c.recvq.dequeue()if sg == nil {break}if sg.elem != nil {typedmemclr(c.elemtype, sg.elem)sg.elem = nil}if sg.releasetime != 0 {sg.releasetime = cputicks()}gp := sg.ggp.param = nilif raceenabled {raceacquireg(gp, c.raceaddr())}glist.push(gp)}// release all writers (they will panic)for {sg := c.sendq.dequeue()if sg == nil {break}sg.elem = nilif sg.releasetime != 0 {sg.releasetime = cputicks()}gp := sg.ggp.param = nilif raceenabled {raceacquireg(gp, c.raceaddr())}glist.push(gp)}unlock(&c.lock)// Ready all Gs now that we've dropped the channel lock.for !glist.empty() {gp := glist.pop()gp.schedlink = 0goready(gp, 3)}}复制代码
关闭的主要流程如下所示:- 获取全局锁;
- 设置channel数据结构chan的关闭标志位;
- 获取当前channel上面的读goroutine并链接成链表;
- 获取当前channel上面的写goroutine然后拼接到前面的读链表后面;
- 释放全局锁;
- 唤醒所有的读写goroutine 。
资料
- Golang-Channel原理解析
- golang对于 nil通道 close通道你所不知道的神器特性
- Go语言make和new关键字的区别及实现原理
- Go底层引用实现
- 图解Golang的channel底层原理
- go内置函数make
- Golang并发调度的GMP模型
推荐阅读
- 一文搞懂GoLang定时器实现原理
- 彻底搞懂虚拟地址翻译为物理地址的过程
- 一文看懂USB4
- 彻底搞懂虚拟内存,虚拟地址,虚拟地址空间
- 如何彻底搞懂Mysql事务原理
- MySQL高级进阶:关于InnoDB存储结构,一文深入分析讲解
- 帮你彻底搞懂 JS 中的 prototype、__proto__与constructor
- Java NIO的三大核心配件:通道、缓冲区、选择器
- 一文看懂技术中台、研发中台、移动中台建设全攻略
- 一文弄清Python网络爬虫解析库!内含多个实例讲解