面试必问
小编作为一个Golang程序员,出道以来,大大小小的面试也经过了几十场了。要说Golang最经典的面试题,非下题莫属。
问题:单核 CPU,开两个 Goroutine,但是其中有一个是死循环,结果会怎么样?
小编也不废话了,直接说结果吧,此处面试官想考察的点是求职者是否关注Golang的版本发展。不同的版本结果不同的 ,分界线为Golang1.14。1.14提出了一个新的调度方式,即 基于信号的抢占式调度 。
废话不多说,直接上代码。代码很简单就不解释了。我们直接看不同版本的。
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
runtime.GOMAXPROCS(1) // 模拟单核 CPU
go func() { // 模拟 Goroutine 死循环
for {
}
}()
time.Sleep(time.Millisecond) // 防止主线程提前退出
fmt.Println("更多免费资料,关注公众号:不穿格子衫的程序猿")
}
Golang(1.13.5)
首先我们先看低版本的。此处小编偷懒了一下下,本地没有安装低版本。直接找了一个在线的IDE。各位看官大人也可以这么干,只需要保证在1.14以下即可。可以看到的是直接超时了。此处就是程序陷入的死循环中。没有跳出。
Golang(1.17.2)
接下来使用的是小编本地的IDE,版本是1.17.2。此处请允许小编废话一下,小编使用的IDE其实是自己搭建的,本质是vim,有兴趣的小伙伴可以弄一下,是真的香。言归正传,此处显然和上面的结果是不一样的。什么原因呢,根本原因就是调度方式的不同。
调度方式
众所周知,Golang的调度核心是GMP,这个是Golang最基本的知识点了,也是面试的必问知识点,此处不过多赘述了。其实 Golang 在之前的版本中已经实现了抢占式调度,不管是陷入到长时间的计算还是系统调用,大多可被 sysmon扫描并进行抢占。但有些极端场景是无法抢占成功的。比如 for {} 等,这类操作无法进行newstack、morestack、syscall,所以无法检测 stackguard0 = stackpreempt。因此无法实现抢占式调度。
所以在 1.14 中加入了基于信号的抢占式调度。大致原理如下,首先是注册绑定 SIGURG 信号及处理方法 runtime.doSigPreempt,sysmon 会间隔性检测超时的 p,然后发送信号,m 收到信号后休眠执行的 goroutine 并且进行重新调度。
绑定相应的 runtime.doSigPreempt 抢占方法:
const sigPreempt = _SIGURG
func initsig(preinit bool) {
for i := uint32(0); i < _NSIG; i++ {
fwdSig[i] = getsig(i)
...
setsig(i, funcPC(sighandler)) // 注册信号对应的回调方法
}
}
// 执行抢占
func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
...
if sig == sigPreempt && debug.asyncpreemptoff == 0 {
// 执行抢占
doSigPreempt(gp, c)
}
}
同时在调度的 runtime.sysmon 方法会调用 retake 方法处理以下两种场景:
抢占阻塞在系统调用上的 P。
抢占运行时间过长的 G。
所以前面我们所说的死循环就迎刃而解了。
面试资料
前面小编经过近一个月的整理,终于将毕生所学,以及毕生收藏,汇聚成册。其中Golang面试资料如下:
说了这么多,各位看官大人要怎么获取呢。很简单, 关注小编,私信 「 资料 」即可获得免费获取方式。
如果大家有比较好的资源欢迎相互交流,共同提高。同时有部分素材来源于网络,如侵,联删 。