当前位置:  首页>> 技术小册>> GO面试指南

在 Go 语言中, GMP 模型中的 P(processor) 管理了若干个 Goroutine ,而这些 Goroutine 最终都运行在 M(machine)上。在 GMP 模型中,当一个 P 管理的 Goroutine 阻塞时,为了让其他 Goroutine 继续运行,这个 P 就会从其他 P 管理的 Goroutine 中偷取一些 Goroutine,然后放到自己的队列中运行。

在这个偷取 Goroutine 的过程中,由于 Goroutine 的状态是通过轻量级的原子操作来改变的,而不是通过加锁来保护的,所以偷取 Goroutine 不需要加锁。

下面是一个简单的示例代码,演示了当一个 Goroutine 阻塞时,偷取 Goroutine 的过程:

  1. package main
  2. import (
  3. "fmt"
  4. "runtime"
  5. )
  6. func main() {
  7. runtime.GOMAXPROCS(2) // 设置 P 的数量为 2
  8. ch := make(chan int, 2)
  9. go func() {
  10. for i := 0; i < 10; i++ {
  11. ch <- i
  12. fmt.Println("send: ", i)
  13. }
  14. }()
  15. go func() {
  16. for {
  17. select {
  18. case i := <-ch:
  19. fmt.Println("recv: ", i)
  20. }
  21. }
  22. }()
  23. select {}
  24. }

在这个示例中,我们启动了两个 Goroutine ,其中一个往一个带缓冲的通道中发送数值,另一个从通道中接收数值并打印。由于通道中有缓冲,所以发送操作不会阻塞,但是接收操作会一直阻塞,直到通道中有数据。

在这个过程中,由于只有一个 P ,所以一旦接收 Goroutine 阻塞,整个程序就会阻塞。为了解决这个问题,Go 运行时系统会自动将这个阻塞的 Goroutine 从当前 P 的队列中偷取,并放到另一个 P 的队列中运行,这样就可以让程序继续运行。在这个过程中,并没有加锁的操作,而是使用了轻量级的原子操作来实现。


该分类下的相关小册推荐: