Golang中select用法导致CPU占用100%的问题分析

上一节(golang中有关select的几个知识点)中介绍了一些对于select{}的一些用法,今天介绍一下有关select在for语句中由于使用不当引起的CPU占用100% 的案例。

先看代码

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int, 10)

	// 读取chan
	go func() {
		for {
			select {
			case i := <-ch:
				// 只读取15次chan
				fmt.Println(i)
			default:
			}
		}
	}()

	// 写入10个值到chan
	for i := 0; i < 15; i++ {
		ch <- i
	}

	// 模拟程序效果使用
	time.Sleep(time.Minute)
}

实现功能

通过操作chan来实现消费者逻辑。

问题现象

但在运行的时候,即发现CPU占用率100%,下面我们分析一下什么原因引起的。

问题分析

程序运行时,先使用go关键字创建一个 goroutine,里面是一个for循环语句。for语句里面通过select{}来监听是否有chan的IO操作,当ch中有可以读取的数据时,则将值打印出来。没有的话则执行default语句,而这里default语句为空,所以继续下一次for语句,for{}是一个死循环语句。

当读取15次ch后,由于ch会永远处于阻塞状态,所以会一直执行default条件,然后再执行for循环。此时这段逻辑基本演变成了一个空的 for{} 语句,所以会导致CPU战胜100%。

解决办法

既然我们知道这个goroutine会一直占用cpu不放,我们只需要让当前goroutine出让CPU控制权给其它goroutine即可。根据GMP调度原理,这里我们只需要让操作chan的IO语句进行阻塞即可,这样P就可以继续寻找下一个goroutine执行了,等这个G和M有数据交互发生的时候,再找一个P继续执行就行了。这里实现方法很简单就注释掉 default 语句即可。