golang之channel进阶

golang 之 channel 并发访问

多图详解Go中的Channel源码

Channel 在运行时的内部表示是 runtime.hchan,该结构体中包含了一个用于保护成员变量的互斥锁,从某种程度上说,Channel 是一个用于同步和通信的有锁队列。

有很多试图通过各种方式 实现 无锁 Channel 的方案,但目前都还有各种各样问题尚不够完美.

所以实际上也借助了锁 (sync.Mutex)


Go 语言的 Channel 在运行时使用 runtime.hchan 结构体表示。在 Go 语言中创建新的 Channel 时,实际上创建的都是该结构体:

src/runtime/chan.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
}

以下内容来自 Golang Channel 实现原理

hchan:channel 的底层数据结构

• qcount:当前 channel 中存在多少个元素;(q为queue的缩写)

• dataqsize: 当前 channel 能存放的元素容量;(环形队列的容量)

• buf:channel 中用于存放元素的环形缓冲区;

• elemsize:channel 元素类型的大小;

• closed:标识 channel 是否关闭;(为什么不用bool,或者int8

• elemtype:channel 元素类型;

• sendx:发送元素进入环形缓冲区的 index;

• recvx:接收元素所处的环形缓冲区的 index;

• recvq:因接收而陷入阻塞的协程队列;

• sendq:因发送而陷入阻塞的协程队列;


通过什么实现阻塞?

1
2
3
4
type waitq struct {
first *sudog
last *sudog
}

waitq:阻塞的协程队列

• first:队列头部

• last:队列尾部

1
2
3
4
5
6
7
8
9
10
11
type sudog struct {
g *g

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

isSelect bool

c *hchan
}

sudog:用于包装协程的节点

• g:goroutine,协程;

• next:队列中的下一个节点;

• prev:队列中的前一个节点;

• elem: 读取/写入 channel 的数据的容器;

• isSelect:标识当前协程是否处在 select 多路复用的流程中;

• c:标识与当前 sudog 交互的 chan.

深入 Go 并发原语 — Channel 底层实现

Go 语言设计与实现-Channel

Go Channel 应用模式

文章目录