您的位置 首页 golang

Go面试必问——单核 CPU,两个 Goroutine,一个死循环

面试必问

小编作为一个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面试资料如下:

说了这么多,各位看官大人要怎么获取呢。很简单, 关注小编,私信 资料 」即可获得免费获取方式。

如果大家有比较好的资源欢迎相互交流,共同提高。同时有部分素材来源于网络,如侵,联删

文章来源:智云一二三科技

文章标题:Go面试必问——单核 CPU,两个 Goroutine,一个死循环

文章地址:https://www.zhihuclub.com/98574.shtml

关于作者: 智云科技

热门文章

网站地图