Golang中的gopark和goready
在Go语言的运行时系统中,gopark和goready是用于协调Goroutine调度的内部函数。它们通常由运行时系统自动管理,一般开发者不需要直接调用。以下是它们的核心作用和原理:
1. gopark:暂停当前Goroutine
- 功能:
gopark是一个运行时内部函数,用于将当前Goroutine设置为等待状态(如阻塞状态),并将其与当前线程分离。此时,调度器会回收当前线程,用于执行其他可运行的Goroutine。 - 触发场景:
当Goroutine需要等待某个事件时(如等待通道读写、定时器到期、I/O完成等),运行时系统会自动调用gopark。例如:ch := make(chan int) go func() { <-ch // 接收数据时,若通道为空,当前Goroutine会被gopark挂起 }() - 本质:
释放当前线程资源,使调度器可以复用线程运行其他Goroutine。
2. goready:唤醒等待中的Goroutine
- 功能:
goready是配套的内部函数,用于将处于等待状态的Goroutine标记为可运行状态(放入调度器的运行队列),等待线程调度执行。 - 触发场景:
当等待的条件满足时,运行时系统会调用goready。例如,向通道发送数据时,若接收方Goroutine因等待被gopark挂起,发送操作会触发goready唤醒接收方:ch <- 42 // 发送数据时,若有接收方被挂起,会调用goready唤醒它 - 本质:
重新激活被挂起的Goroutine,使其能够继续执行。
协同工作机制
- 挂起与回收:
当一个Goroutine因条件未满足被gopark挂起时,运行时会将线程资源让给其他Goroutine,避免忙等待。 - 条件满足时唤醒:
当依赖的条件满足(如通道数据就绪、锁可用等),运行时通过goready将被挂起的Goroutine重新标记为可运行状态,加入调度队列等待执行。
注意事项
- 内部函数:
gopark和goready是runtime包的未导出函数,开发者无法直接调用(强行使用可能破坏调度逻辑)。 - 调度透明性:
Go的调度器自动处理挂起和唤醒,开发者只需关注并发语义(如select、channel、sync.Mutex等),无需操作底层函数。
示例场景:通道操作
ch := make(chan int)
// Goroutine 1:接收数据
go func() {
val := <-ch // 若通道空,被gopark挂起
fmt.Println(val)
}()
// Goroutine 2:发送数据
go func() {
ch <- 42 // 发送数据后,触发goready唤醒接收方
}()
发送数据时,若接收方Goroutine被挂起,运行时通过goready将其唤醒,使其返回运行队列并最终输出42。
总结
- gopark:将Goroutine挂起并释放线程资源,优化并发效率。
- goready:在条件满足时唤醒挂起的Goroutine,保障逻辑继续执行。
- Go的调度器通过这两者的配合,实现了高效的协作式调度(M:N调度模型)。开发者只需基于高级抽象(如通道、锁)编写代码,底层细节由运行时系统自动处理。
Do not communicate by sharing memory; instead, share memory by communicating.

浙公网安备 33010602011771号