GO 中 Chan 实现原理分享

本文深入探讨GO中Chan的实现原理,从 Chan 的概念、底层数据结构到创建与读写流程,以及SELECT的简单应用。通过源码分析,解释了Chan如何作为并发的通信机制,包括环形队列、协程、互斥锁等关键元素,同时讨论了通道关闭的异常情况及SELECT的使用。
摘要由CSDN通过智能技术生成

GO 中 Chan 实现原理分享

嗨,我是小魔童哪吒,还记得咱们之前分享过GO 通道 和sync包的使用吗?咱们来回顾一下

  • 分享了通道是什么,通道的种类
  • 无缓冲,有缓冲,单向通道具体对应什么
  • 对于通道的具体实践
  • 分享了关于通道的异常情况整理
  • 简单分享了sync包的使用

要是对上述内容还有点兴趣的话,欢迎查看文章 GO通道和 sync 包的分享

chan 是什么?

是一种特殊的类型,是连接并发goroutine的管道

channel 通道是可以让一个 goroutine 协程发送特定值到另一个 goroutine 协程的通信机制

通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序,这一点和管道是一样的

一个协程从通道的一头放入数据,另一个协程从通道的另一头读出数据

每一个通道都是一个具体类型的导管,声明 channel 的时候需要为其指定元素类型。

本篇文章主要是分享关于通道的实现原理,关于通道的使用,可以查看文章 GO通道和 sync 包的分享 ,这里有详细的说明

GO 中 Chan 的底层数据结构

了解每一个组件或者每一个数据类型的实现原理,咱们都会去看源码中的数据结构是如何设计的

同样,我们一起来看看 GO 的 Chan 的数据结构

GO 的 Chan 的源码实现是在 : src/runtime/chan.go

type hchan struct {
   
   qcount   uint           // total data in the queue
   dataqsiz uint           // size of the circular queue
   buf      unsafe.Pointer // points to an array of dataqsiz elements
   elemsize uint16
   closed   uint32
   elemtype *_type // element type
   sendx    uint   // send index
   recvx    uint   // receive index
   recvq    waitq  // list of recv waiters
   sendq    waitq  // list of send waiters

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

hchan 是实现通道的核心数据结构,对应的成员也是不少,咱们根据源码注释一个参数一个参数的来看看

tag 说明
qcount 当前的队列,剩余元素个数
dataqsiz 环形队列可以存放的元素个数,也就是环形队列的长度
buf 指针,指向环形队列
elemsize 指的的队列中每个元素的大小
closed 具体标识关闭的状态
elemtype 见名知意,元素的类型
sendx 发送队列的下标,向队列中写入数据的时候,存放在队列中的位置
recvx 接受队列的下标,从队列的 这个位置开始读取数据
recvq 协程队列,等待读取消息的协程队列
sendq 协程队列,等待发送消息的协程队列
lock 互斥锁,在 chan 中,不可以并发的读写数据

根据上面的参数,我们或多或少就可以知道 GO 中的通道实现原理设计了哪些知识点:

  • 指针
  • 环形队列
  • 协程
  • 互斥锁

我们顺便再来看看上述成员的协程队列 waitq 对应的是啥样的数据结构

type waitq struct {
   
   first *sudog
   last  *sudog
}

sudog 结构是在 src/runtime/runtime2.go中 ,咱们顺便多学一手

// sudog represents a g in a wait list, such as for sending/receiving
// on a channel.
type sudog struct {
   
   // The following fields are protected by the hchan.lock of the
   // channel this sudog is blocking on. shrinkstack depends on
   // this for sudogs involved in channel ops.

   g *g

   next *sudog
   prev *sudog
   elem unsafe.Pointer // data element (may point to stack)

   // The following fields are never accessed concurrently.
   // For channels, waitlink is only accessed by g.
   // For semaphores, all fields (including the ones above)
   // are only accessed when holding a semaRoot lock.

   acquiretime int64
   releasetime int64
   ticket      uint32

   // isSelect indicates g is participating in a select, so
   // g.selectDone must be CAS'd to win the wake-up race.
   isSelect bool

   // success indicates whether communication over channel c
   // succeeded. It is true if the goroutine was awoken because a
   // value was delivered over channel c, and false if awoken
   // because c was closed.
   success bool

   parent   *sudog // semaRoot binary tree
   waitlink *sudog // g.waiting list or semaRoot
   waittail *sudog // semaRoot
   c        *hchan // channel
}

根据源码注释,咱们大致知道sudog 是干啥的

Sudog表示等待列表中的 g,例如在一个通道上发送/接收

Sudog是很必要的,因为g↔synchronization对象关系是多对多

一个 g 可能在很多等候队列上,所以一个 g 可能有很多sudogs

而且许多 g 可能在等待同一个同步对象,所以一个对象可能有许多sudogs

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值