返回

终于整明白了,这次终于看懂Go面试题为什么90%的人都答错!

后端

传说中的面试题

据说有一道Go面试题,90%的人都答错了。这道题是这样的:

package main

import (
	"fmt"
	"runtime"
)

func main() {
	runtime.GOMAXPROCS(2)
	go test(1)
	go test(2)
	go test(3)

	fmt.Println("main routine")
}

func test(id int) {
	fmt.Println(id)
}

这道题的目的是考察对Go并发编程的理解,尤其是对Go协程的调度机制的理解。

错误的答案

大多数人回答这道题时,都会给出这样的答案:

  • 这道题的输出结果是:
1
2
3
main routine
  • 这是因为Go的协程是并发的,因此这三个协程会同时执行。

  • 当这三个协程执行完毕后,主协程才会执行,因此最后输出“main routine”。

正确的答案

然而,这个答案是错误的。正确的答案是:

  • 这道题的输出结果可能是:
1
3
main routine
2
  • 这是因为Go的协程并不是真正意义上的并发,而是伪并发。

  • Go的协程实际上是在一个共享的内存空间中运行的,因此它们之间是存在竞争的。

  • 在这种情况下,三个协程都试图同时访问fmt.Println函数,因此可能会发生竞争。

  • 当一个协程获得了对fmt.Println函数的访问权时,其他协程就会被阻塞。

  • 因此,这三个协程的输出顺序可能会是任意的。

如何理解这个答案

为了理解这个答案,我们需要对Go的并发编程机制有一个基本的了解。

  • 在Go中,并发编程是通过协程来实现的。

  • 协程是一种轻量级的线程,它与线程类似,但它比线程更轻量级,因此可以创建更多的协程。

  • 协程之间共享同一个内存空间,因此它们之间可以进行通信和协作。

  • 然而,协程并不是真正意义上的并发,而是伪并发。

  • 这是因为Go的协程实际上是在一个共享的内存空间中运行的,因此它们之间是存在竞争的。

  • 当一个协程获得了对共享资源的访问权时,其他协程就会被阻塞。

  • 因此,这三个协程的输出顺序可能会是任意的。

断点调试

为了进一步理解这个答案,我们可以使用断点调试来观察这三个协程的执行过程。

  • 在GoLand中,我们可以通过在代码行号前添加断点来进行断点调试。

  • 当程序执行到断点时,它会停止执行,并允许我们检查变量的值。

  • 通过断点调试,我们可以看到这三个协程的执行顺序是任意的。

  • 这也证实了我们的猜测,即Go的协程并不是真正意义上的并发,而是伪并发。

总结

通过这道面试题,我们学习到了以下几点:

  • Go的并发编程是通过协程来实现的。

  • 协程是一种轻量级的线程,它与线程类似,但它比线程更轻量级,因此可以创建更多的协程。

  • 协程之间共享同一个内存空间,因此它们之间可以进行通信和协作。

  • 然而,协程并不是真正意义上的并发,而是伪并发。

  • 这是因为Go的协程实际上是在一个共享的内存空间中运行的,因此它们之间是存在竞争的。

  • 当一个协程获得了对共享资源的访问权时,其他协程就会被阻塞。

  • 因此,这三个协程的输出顺序可能会是任意的。