for-select default分支 空值,导致cpu跑满100%

225 阅读1分钟

下面这段代码会把cpu跑满

func main()  {
	var ch chan int
	for {
		select {
		case <-ch:
			return
		default:
		}
	}
}

对于select语句,每个case的IO事件都是阻塞的,监听IO事件是不会占用CPU至满的。造成CPU占用的原因是这个空default,因为当case的条件不满足时,循环将会走default,然后执行下一个循环,这就造成了死循环,因此在使用for-select语句的时候不能定义空的default

通过pprof http方法分析 go tool pprof -http=:9091 profile

image.png 方框越大代表函数执行的时间越久

通过命令行分析

localhost :: ~/Desktop » go tool pprof  profile                                                                                               1 ↵
File: cronservice
Type: cpu
Time: Mar 8, 2023 at 5:39pm (CST)
Duration: 1.10s, Total samples = 1.47s (133.21%)
Entering interactive mode (type "help" for commands, "o" for options)

使用top排序分析 
(pprof) top
Showing nodes accounting for 1.47s, 100% of 1.47s total
      flat  flat%   sum%        cum   cum%
     0.51s 34.69% 34.69%      0.77s 52.38%  runtime.chanrecv
     0.48s 32.65% 67.35%      1.25s 85.03%  runtime.selectnbrecv
     0.26s 17.69% 85.03%      0.26s 17.69%  runtime.empty (inline)
     0.21s 14.29% 99.32%      1.46s 99.32%  cronservice/internal/biz.(*CronTabHandler).Run
     0.01s  0.68%   100%      0.01s  0.68%  runtime.casgstatus
         0     0%   100%      1.46s 99.32%  cronservice/internal/biz.(*TaskBiz).ListenTaskStart.func1
         0     0%   100%      0.01s  0.68%  runtime.gopreempt_m
         0     0%   100%      0.01s  0.68%  runtime.goschedImpl
         0     0%   100%      0.01s  0.68%  runtime.mcall

使用list func函数名 具体分析某一个函数         
(pprof) list runtime.selectnbrecv
Total: 1.47s
ROUTINE ======================== runtime.selectnbrecv in /usr/local/go/src/runtime/chan.go
     480ms      1.25s (flat, cum) 85.03% of Total
         .          .    702://	if selected, ok = selectnbrecv(&v, c); selected {
         .          .    703://		... foo
         .          .    704://	} else {
         .          .    705://		... bar
         .          .    706://	}
     130ms      130ms    707://
     240ms      1.01s    708:func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) {
     110ms      110ms    709:	return chanrecv(c, elem, false)
         .          .    710:}
         .          .    711:
         .          .    712://go:linkname reflect_chansend reflect.chansend
         .          .    713:func reflect_chansend(c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
         .          .    714:	return chansend(c, elem, !nb, getcallerpc())

可以看到从底层来说问题就出现708和709行

最简单的解决办法默认分支default 里面休眠几秒

链接:

zhuanlan.zhihu.com/p/301065345

www.cnblogs.com/faithfu/p/1…