Go 语言中让某个协程无限堵塞的几种方式.
Go 语言中让某个协程无限堵塞的几种方式.
Go 语言自带的 goroutine 特性会让我们需要在某些时候由 main
函数启动一些协程之后就无限阻塞, 让别的协程处理问题. 以下是一些方式:
死循环法
1 | for {;} |
使用这种方式会让你的程序 随机暴毙, 无法 Debug 找到问题出在哪儿.
原因: go 语言中的 goroutine 并不是线程, 当且仅当遇到系统调用和io操作等时候会主动让出自己的时间片等待外部数据. 但是, 当程序运行死循环的时候 并没有任何io操作, 不会主动让出自己的时间片. 又由于线程调度的原因, 程序会在一个随机时间进入死循环执行, 导致整个程序所有协程全部卡死.
Update: 新的版本的go语言中提供了抢占式的协程调度,不会出现这个卡死的问题。但是仍然不推荐使用这种方式。为什么要浪费CPU时间呢
不仅如此, 由于系统会在别的协程进行io操作的时候进行协程调度, 因此 表面上观察到的现象是别的协程在进行io操作的时候堵死整个程序 , 几乎无法找到bug在哪儿 🌝
死循环让出时间片法
1 | for { |
死循环执行 runtime.Gosched()
这一函数, 执行这个函数会让出当前协程的时间片, 稍后再回到此协程. 不会出bug, 但是效率偏低. (因为仍然在不断地切换到这个协程.)
死循环Sleep法
1 | for { |
同上, 仍然有轻微的(但完全可以忽略)效率问题, 但是这样实现十分不优雅.
阻塞的读取空数据法:
1 | blockChan := make(chan int) |
这种方法创建一个不会有数据的 Channel
, 并试图从中读取数据. 因为永远读不到数据, 因此会堵塞这个协程.
无效率问题, 但是实现仍然不优雅.
WaitGroup法:
1 | w := sync.WaitGroup{} |
创建一个WaitGroup
, 调用 wait
方法无限等待. 无效率问题, 但是仍然不优雅(要写三行).
Select法:
1 | select{} |
select
被用于同步或异步的从Channel
中读取数据. 因为这里没有指定任何Channel
, 同时没有default
来进行异步读取, 程序会阻塞的试图从没有channel
中读取数据.
没有创建任何变量, 0内存开销, 同时调度器也再也不会调度这个协程, 也同样没有性能开销.
(关键是只要一行)