一文搞懂Go通道( 四 )

向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休眠
关闭channelfunc 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 。
总结了解一下具体实现还是很好的,虽然在使用上不会带来变化,不过理解了内涵后,能够更加灵活地使用通道,可以更加容易的追查到问题,也能学习到高手的设计思想 。
资料
  1. Golang-Channel原理解析
  2. golang对于 nil通道 close通道你所不知道的神器特性
  3. Go语言make和new关键字的区别及实现原理
  4. Go底层引用实现
  5. 图解Golang的channel底层原理
  6. go内置函数make
  7. Golang并发调度的GMP模型
最后大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)




推荐阅读