深入理解 Go channel
Go 语言的 Channel 关键字是实现其原生并发编程的关键组成,你可能听说过 “不要通过共享内存的方式通信,要通过通信的方式共享内存”,这句话正是针对 Channel 说的。
Go 语言倡导通过通信的方式共享内存,也就是减少锁的使用,尽量都通过 Channel 来进行协程间通信。
其实这被称为一种并发模型名为 CSP(Communicating Sequential Processes),即通信顺序进程。
两个实体 goroutine 通过 Channel 通信,收发消息。
数据结构
Channel 是一种 带锁环状队列,运行时结构定义在 runtime/chan.hchan ,代码也比较容易理解。
type hchan struct {
qcount uint // 队列中元素数量
dataqsiz uint // 环形队列总大小
buf unsafe.Pointer // 底层数组指针
elemsize uint16 // 元素大小
closed uint32 // 是否已关闭
elemtype *_type // 元素类型
sendx uint // 发送端下标
recvx uint // 接收端下标
recvq waitq // 接收端等待队列
sendq waitq // 发送端等待队列
// 保护 hchan 并发访问的字段
lock mutex
}
type waitq struct {
first *sudog // 等待队列链表的头节点
last *sudog // 尾节点
}
通过两个指针指向发送端和接收端接下来的操作位置,结合锁机制完成数据操作,这是很容易想到的并发处理方式。runtime/chan.waiq 是阻塞等待 goroutine 的双向链表,每一个节点是一个封装了的 goroutine,被命名为 sudog
,定义在 runtime/runtime2.go 。
type sudog struct {
g *g
next *sudog
prev *sudog
elem unsafe.Pointer // data element (may point to stack)
...
}
next
和 prev
就是指向前后节点的指针,并且包裹了一个 goroutine 结构。