MENU

Go读书笔记:channel通道

April 25, 2022 • Golang

序言

channel是golang在语言层面提供的goroutine间的通信方式

channel数据结构

type hchan struct {
  qrcount  uint           // 当前队列中剩余的元素个数
  dataqsiz uint           // 环形队列长度,即可以存放的元素个数
  buf      unsafe.Pointer // 环形队列指针
  elemsize uint16         // 每个元素的大小
  closed   uint32         // 标识关闭状态
  elemtype *_type         // 元素类型
  sendx    uint           // 队列下标,指示元素写入时存入队列的位置
  recvx    uint           // 队列下标,指示元素从队列的该位置读出
  recvq    waitq          // 等待读消息的goroutine队列
  sendq    waitq          // 等待写消息的goroutine队列
  lock     mutex          // 互斥锁,chan不允许并发读写
}

环形队列

值得注意的是,channel内部实现了一个环形队列来作为其缓冲区,队列的长度是创建channel时指定的

等待队列

当channel的缓冲区为空或者没有缓冲区时,当前的goroutine会被阻塞,向channel中写数据,如果channel缓冲区已满或者没有缓冲区,当前goroutine会被阻塞,被阻塞的goroutine会被放到channel的等待队列中

  1. 因为读阻塞的goroutine会被向channel写入数据的goroutine唤醒
  2. 因为写阻塞的goroutine会被向channel读数据的goroutine唤醒

一般情况下,recvq和sendq至少有一个为空,除非是同一个goroutine使用select语句向channel中一边写数据一边读数据

类型信息

一个channel只能传递一种类型的值,类型信息存储在hchan数据结构中

  1. elemtype代表类型,用于数据传递过程中的赋值
  2. elemsize 代表类型大小,用于在buf中定位元素位置

一个channel同时仅允许被一个goroutine读写

channel读写

创建channel

channel的过程实际上是初始化hchan结构,其中类型信息和缓冲区长度由make传入,buf的大小则与元素大小和缓冲区长度共同决定

向channel写数据

为了解释清这个读写过程,这里我们假设一个场景,我们假设每一个写数据的goroutine是一个来送快递的快递小哥,每一个来读数据的goroutine是一个来取快递的人,然后缓冲区就是快递点的桌子(这个桌子可有可无),那么写数据就变成了如下的一个过程:

  1. 快递小哥到达快递点,发现有人在排队取快递,快递小哥直接把快递给在队列中的人,然后叫醒(唤醒)他,快递小哥结束送快递过程
  2. 快递小哥到达快递点,发现桌子上还有空位置,直接把快递放在桌子上,结束发送快递过程
  3. 快递小哥到达快递点,发现桌子没有空余位置了,快递小哥进入等待发送队列中,拿着快递就开始睡大觉,等待被别人唤醒

作者:NorthCity1984
出处:https://grimoire.cn/golang/channel.html
版权:本文《Go读书笔记:channel通道》版权归作者所有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任