携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
Go Channel
Go 核心的数据结构和 GoRoutine 之间的通信方式, Channel 是支撑 Go 语言高性能并发编程模型的重要结构。一个通道相当于一个先入先出的(FIFO) 的队列,也就是说通道中各个的元素值都是严格按照都顺序排列的。初始化一个通道时,需要用到 Go 内建函数 make, 同时还要确定通道的类型。
Channel
Channel 也称为管道。 Go 语言中最常说的一句话 “不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存”, 在 Java 中,多个线程传递数据一般都是通过共享内存的方式。
Goroutne 使用 Channel 传递数据, 一个会向 Channel 中发送数据,另外一个会从 Channel 中接收数据,他们两个能够独立运行,并不存在直接联系,但是能通过 Channel 完成通信。
Channel 读写规则 - 先入先出 FIFO
-
先从 Channel 读取数据的 Goroutine 会先接收到数据。
-
先向Channel 发送数据的 Goroutine 会得到先发送数据的权利
-
发送方会向缓冲区写入数据, 然后唤醒接收方, 多个接收方会尝试从缓冲去中读取数据,如果没有读到数据,会重新休眠
-
接收方会从缓冲去中读取数据,然后唤醒发送方, 发送方尝试向缓冲区中写入数据,如果缓冲区满,会重新陷入休眠
无锁管道 - channel
锁是一种常见的并发控制技术,一般会将锁分成乐观锁和悲观锁。即乐观并发控制和悲观并发控制, 无锁(lock-free)队列更准确的描述是使用乐观并发控制的队列,乐观并发控制也叫乐观锁。
Channel 在运行内部表示是 runtime.hchan, 结构中包含了用于包含成员变量的互斥锁,某种程度上说 channel 是一个用于同步和通信的有锁队列。使用互斥锁解决了程序中的线程竞争问题。
- 同步 Channel - 不需要缓冲区,发送放回直接交给 Handoff 接收方
- 异步 Channel - 基于环形缓冲的传统生产者消费者模型
- chan struct{} 类型的异步 Channel - Struct{} 类型不占用内存空间,不需要事先缓冲区和直接发送 Handoff
数据结构
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
}