go version go1.22.5
Golang以高性能以及天然的并发的能力,通过轻量级的Goroutine和通道channel,使得编写高并发程序更加容易和安全。
这篇文章来解读Golang当中的Channel
顾名思义,Channel就是一个通信管道,被设计用于实现Goroutine之间的通信。 Go的并发哲学是CSP模型,基于channel实现,CSP的全程是“Communicating Sequential Processes”,这个模型的解释就是:不要通过共享内存来通信,而要通过通信来实现内存共享
Channel的数据结构
channel的功能强大并且复杂,不会是几个字节就能实现的,由一个结构体来承接channel的作用,这个结构体是hchan结构体
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
}
qcount: queue(环形数组)的数量dataqsiz: queue(环形数组)的容量buf: 指向存储channel数据的queue(环形数字)的指针elemsize: 元素的大小,大小在计算机系统当中就是字节数closed: channel是否关闭sendx: 下一次写的位置recvx: 下一读的位置sendq:等待发送的queuerecvq: 等待接受的queuelock:并发锁,保证channel数据结构的并发安全
深入理解Channel的数据结构
在创建channel的时候,会有一个环形的数据结构来存储Channel当中的数据,相当于一个buf的缓存,因为他是一个环形的数据结构,所以会有sendx和recvx分别指向下一次写和下一次读,也就是在Channel读写操作的时候提供定位的功能
qcount则是用来表示buf当中的元素,而dataqsiz表示buf的容量
同时Channel和Goroutine是Go并发编程的杀器,Channel会在多个goroutine之间进行通信,而lock是用来保护读写以及关闭的并发安全
因为在Go当中会有阻塞读和阻塞写的概念,Channel当中会有读写等待队列(recvq和sendq)来帮助goroutine在未完成读写操作后,可以被阻塞挂起,然后等待Channel通信到来的时候,再被唤醒
recvq and sendq
recvq和sendq是 waitq类型
type waitq struct {
first *sudog //sudo 队列的队头指针
last *sudog //sudo 队列的队尾指针
}
sudog可以看作是对阻塞挂起的g的一个封装。然后用多个sudog来构成等待队列
以下是sudog结构源码:
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)
// 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
c *hchan // channel
}
- g:绑定的goroutime
- next:后指针
- prev:前指针
- elem作为收发数据的容器
- 当向channel发送数据时,elem代表将要写进channel的元素地址
- 当从channel读取数据时,elem代表要从channel中读取的元素地址
- isSelect:标识是不是因为select操作封装的sudog
- success
- true:表示这个sudog是因为channel通信唤醒的
- 否则为false,表示这个sudog是因为channel close唤醒的