Go-channel(管道)底层实现总结
channel的构成
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 // 发送者等待队列 // lock 保护上述所有字段 lock mutex }
-
-
发送操作:当一个 goroutine 尝试向无缓冲
channel
发送数据时,如果没有其他 goroutine 在接收,发送操作会阻塞,该 goroutine 会被放入sendq
等待队列。直到有接收者准备好,两个 goroutine 会被调度唤醒,数据直接从发送者转移到接收者,不需要经过缓冲区。 -
接收操作:类似地,当一个 goroutine 尝试从无缓冲
channel
接收数据时,如果没有其他 goroutine 在发送,接收操作会阻塞,该 goroutine 会被放入recvq
等待队列。一旦有发送者准备好,数据直接传递,两个 goroutine 继续执行。
-
-
有缓冲 Channel
-
发送操作:当向有缓冲
channel
发送数据时,首先检查缓冲区是否已满。如果缓冲区未满,数据会被复制到缓冲区的sendx
位置,sendx
递增,qcount
加一。如果缓冲区已满,发送操作会阻塞,发送者 goroutine 被放入sendq
等待队列,直到有接收者从缓冲区取出数据,腾出空间。 -
接收操作:从有缓冲
channel
接收数据时,先检查缓冲区是否为空。如果缓冲区不为空,数据从缓冲区的recvx
位置取出,recvx
递增,qcount
减一。如果缓冲区为空,接收操作会阻塞,接收者 goroutine 被放入recvq
-
-
-
阻塞与唤醒:当一个 goroutine 因
channel
操作阻塞时,它会被放入相应的等待队列(sendq
或recvq
-
-
检测关闭:在接收操作时,可以通过
v, ok := <- ch
的方式来检测channel
是否关闭。如果ok
为false
,则表示channel
已关闭且缓冲区中没有数据了。
type waitq struct { first *sudog last *sudog }
type sudog struct { g *g // 指向对应的 goroutine // 以下字段与 channel 操作相关 selectdone *uint32 next *sudog prev *sudog elem unsafe.Pointer // 指向数据元素(用于发送或接收) acquiretime int64 releasetime int64 ticket uint32 parent *sudog waitlink *sudog waittail *sudog c *hchan // 指向对应的 channel }
发送队列(sendq
)和接收队列(recvq
入队操作
当一个 goroutine 因为 channel
操作需要阻塞时,会创建一个 sudog
实例,将其 g
字段指向当前 goroutine,c
字段指向对应的 channel
。然后根据操作类型(发送或接收),将该 sudog
加入到 sendq
或 recvq
队列中。
出队操作
当 channel
上的操作条件满足,需要唤醒等待队列中的 goroutine 时,会从相应队列(sendq
或 recvq
)中取出 sudog
实例,并唤醒对应的 goroutine。
通过这种双向链表的方式实现发送队列和接收队列,使得在 channel
操作中阻塞和唤醒 goroutine 的管理变得高效且有序,这是 Go 语言实现 channel
并发通信机制的重要基础。
sudog
sudog
是 Go 运行时(runtime)中一个关键的辅助结构体,主要作用是关联被阻塞的 goroutine 与它所等待的资源(如 channel、select
语句等),是实现 goroutine 阻塞 / 唤醒机制的核心载体。sudog
作为管理 goroutine 等待状态的 “通用容器”,channel 只是其典型应用场景之一,所有需要管理 goroutine 等待 / 唤醒的同步机制,都会依赖sudog
实现。它的字段设计围绕 “管理等待上下文” 展开,以下是每个字段的详细解析
1. g *g
2. selectdone *uint32
3. next *sudog
/ prev *sudog
4. elem unsafe.Pointer
5. acquiretime int64
6. releasetime int64
7. ticket uint32
8. parent *sudog
9. waitlink *sudog
/ waittail *sudog
10. c *hchan
总结
sudog
的所有字段都服务于一个核心目标:精准管理 goroutine 的等待上下文。它通过关联 goroutine(g
)、等待的资源(c
)、数据缓冲区(elem
)以及队列关系(next
/prev
),配合 select
同步(selectdone
/ticket
)和调试信息(acquiretime
/releasetime
),实现了 goroutine 阻塞 / 唤醒机制的安全性和高效性。