Go语言数据结构学习之channel | 青训营笔记

55 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第13天。

最近各大厂陆续开始收暑期实习的简历了,作为Go语言选手自然要好好学习一下Go语言相关知识。

channel相关的源代码在runtime/chan.go中。

type hchan struct {
   qcount   uint
   dataqsiz uint
   buf      unsafe.Pointer
   elemsize uint16
   closed   uint32
   elemtype *_type
   sendx    uint
   recvx    uint
   recvq    waitq
   sendq    waitq

   // 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
}

channel的底层实现是一个队列,有一段连续的空间,在创建时根据指定的缓冲区大小分配。

  • qcount是队列中元素个数
  • dataqsiz是队列大小
  • buf指向该队列
  • elemsize为每个元素大小
  • closed表示channel是否被关闭
  • elemtype为元素类型
  • lock为控制互斥访问的锁 图片.png
  • recvx为队列中待接收的元素位置(类似于队首)
  • sendx为往队列中放入元素的为(类似于队尾)
  • recvq为等待接收元素的goroutine
  • sendq为等待放入元素的goroutine

对于发送数据的过程,在不考虑通道关闭等异常情况下,如果通道有缓存且未满,则待发送的数据被存入缓存中(即底层队列的队尾)。

倘若没有缓存且recvq上没有接收者,或者缓存已满,那么发送的goroutine需要阻塞,挂在sendq上,等待有接收的goroutine取出数据腾出位置。

如果没有缓存,或者缓存为空的情况下recvq上有接收者,那么数据就直接从发送goroutine传给接收goroutine,不需要经过通道缓存,提高效率。

对于接收数据的情况也类似。如果通道有缓存且里面有数据,接收goroutine从中取出数据。

如果没有缓存或者缓存为空,且没有发送者在等待,那么就挂在recvq上等待。

如果缓存为空且有发送者在等待,那么就直接从发送者手上取数据。