以下基于 Go 1.14
Go 程序中,在下面几种情况下都有可能触发调度(即调用 runtime.schedule()
): * 创建一个新的线程时 * goroutine 执行结束的时候 * 主动挂起 * 协作式调度 * 基于函数调用的抢占式调度 * 基于信号的抢占式调度(Go 1.14 新特性) * 系统调用
前面已经提到过前面两种,下面看下其他几种情况的行为方式。
主动挂起
当有诸如 time.Sleep()
、锁、channel 之类的操作时,就会主动挂起 goroutine。而 goroutine 的主动挂起和唤醒分别是通过 runtime.gopark()
和 runtime.goready()
实现的:
系统调用
不是所有的系统调用都会触发调度,只有那些需要运行时参与的系统调用(syscall.Syscall()
)才有可能触发调度:
协作式调度
协作式调度指的是 goroutine 主动让出处理器,从而允许其他 goroutine 运行:
抢占式调度
Go 语言的抢占式调度是在分段栈的机制上实现的。编译器会在分段栈上插入函数,这样,所有的 goroutine 在进行函数调用的时候都有机会进入运行时检查是否需要抢占。
但是,这种机制对于诸如轮询计算这种没有函数调用的 goroutine 来说,是无法抢占成功的。因此,Go 1.14 版本引入了基于信号的抢占式调度。