Go 语言中让某个协程无限堵塞的几种方式.

Go 语言中让某个协程无限堵塞的几种方式.

Go 语言自带的 goroutine 特性会让我们需要在某些时候由 main 函数启动一些协程之后就无限阻塞, 让别的协程处理问题. 以下是一些方式:

死循环法

1
for {;}

使用这种方式会让你的程序 随机暴毙, 无法 Debug 找到问题出在哪儿.

原因: go 语言中的 goroutine 并不是线程, 当且仅当遇到系统调用和io操作等时候会主动让出自己的时间片等待外部数据. 但是, 当程序运行死循环的时候 并没有任何io操作, 不会主动让出自己的时间片. 又由于线程调度的原因, 程序会在一个随机时间进入死循环执行, 导致整个程序所有协程全部卡死.
Update: 新的版本的go语言中提供了抢占式的协程调度,不会出现这个卡死的问题。但是仍然不推荐使用这种方式。为什么要浪费CPU时间呢
不仅如此, 由于系统会在别的协程进行io操作的时候进行协程调度, 因此 表面上观察到的现象是别的协程在进行io操作的时候堵死整个程序 , 几乎无法找到bug在哪儿 🌝

死循环让出时间片法

1
2
3
for {
runtime.Gosched()
}

死循环执行 runtime.Gosched() 这一函数, 执行这个函数会让出当前协程的时间片, 稍后再回到此协程. 不会出bug, 但是效率偏低. (因为仍然在不断地切换到这个协程.)

死循环Sleep法

1
2
3
for {
time.Sleep(Time.Hour)
}

同上, 仍然有轻微的(但完全可以忽略)效率问题, 但是这样实现十分不优雅.

阻塞的读取空数据法:

1
2
blockChan := make(chan int)
a := <- blockChan

这种方法创建一个不会有数据的 Channel, 并试图从中读取数据. 因为永远读不到数据, 因此会堵塞这个协程.

无效率问题, 但是实现仍然不优雅.

WaitGroup法:

1
2
3
w := sync.WaitGroup{}
wg.Add(1)
wg.Wait()

创建一个WaitGroup, 调用 wait 方法无限等待. 无效率问题, 但是仍然不优雅(要写三行).

Select法:

1
select{}

select被用于同步或异步的从Channel中读取数据. 因为这里没有指定任何Channel, 同时没有default来进行异步读取, 程序会阻塞的试图从没有channel中读取数据.

没有创建任何变量, 0内存开销, 同时调度器也再也不会调度这个协程, 也同样没有性能开销.

(关键是只要一行)