区分阻塞与系统调用时,Goroutine 会被放在哪里

问题核心:

如果是:

  1. Goroutine 被阻塞(如 channel、锁、cond 等)

  2. Goroutine 进入系统调用(如读文件、网络请求等)

那么它 在变为不可运行(waiting)或从 running 变为 runnable 之后,到底放在哪儿?


✅ 先说:「阻塞」的情况(如:channel、锁等)

这种场景通常是 G 主动等待某个资源,比如:

  • <-ch 读 channel

  • sync.Mutex.Lock()

  • sync.Cond.Wait()

发生了什么?
  1. 当前 G 会从 runningwaiting 状态。

  2. 它不会出现在调度队列中。

  3. 会挂到某个数据结构上,例如:

    • channel 的等待队列(sudog 结构)

    • Mutex 的等待队列(链表)

它不会被放入 P 的 runq,也不会在全局队列中。


被唤醒后(例如 channel 收到值了、锁解锁了):
  • 状态:waitingrunnable

  • 唤醒函数会调用 ready() 函数把它放入调度队列。

放置位置:当前 P 的本地队列(优先),否则全局队列


✅ 再说:「系统调用」的情况(如:read(), select()

发生了什么?
  1. G 进行系统调用,会进入 syscall 状态。

  2. 当前绑定的 M 也被带走去执行系统调用。

  3. P 被解绑,留给别的 M 来复用,保证并发度。

这时候:

  • Gsyscall(不在调度队列中)

  • Msyscall

  • P → 可被抢占、绑定别的 M

系统调用结束(比如 I/O 完成)后:

Go 会检查:

  • 如果有空闲 P,就立即执行它(通过 injectglist() 机制)

  • 否则将该 G 放入 全局 run queue

放置位置:优先尝试立即执行,否则放入 全局队列


总结表格:

情况 状态变化 暂时位置 被唤醒后放置位置
阻塞(channel, lock 等) runningwaiting 资源的等待队列(如 sudog 链) 当前 P 的本地队列 或全局队列
系统调用 runningsyscall 不在 runq,中断当前调度关系 全局队列 或 直接调度执行

你可能感兴趣的:(GMP,go)