我正在学习Go语言,现在有一个问题
我只是在测试缓冲和非缓冲通道。
在为它写测试代码的时候,出现了一个看不懂结果的情况
func BenchmarkUnbufferedChannelInt(b *testing.B) {
ch := make(chan int)
go func() {
for i := 0; i < b.N; i++ {
ch <- i
}
}()
for {
<-ch
}
}
上面的代码没有停止,因为
<-ch
让goroutine永远阻塞。但是,下面的代码不会创建阻止状态并且测试成功。
func BenchmarkUnbufferedChannelInt(b *testing.B) {
ch := make(chan int)
go func() {
for {
<-ch
}
}()
for i := 0; i < b.N; i++ {
ch <- i
}
}
BenchmarkUnbufferedChannelInt
BenchmarkUnbufferedChannelInt-12 8158381 142.1 ns/op
PASS
据我所知,goroutine之间没有父子关系,所以一个goroutine的终止不会影响除main goroutine之外的其他goroutine的终止。
理论上,以上两种代码最终都应该永远被阻止。
但是不知道为什么只有第二个代码成功了
我想知道 Go 是如何调用测试函数的。假设测试函数运行的 goroutine 与 main goroutine 具有相同的特征,我可以理解上面代码的结果。
但如果不是,我不明白。在这两个测试中,goroutine 最终应该永远被阻塞。
你能解释一下上面的结果吗?请!
上面的代码没有停止,因为
让goroutine永远阻塞。<-ch
不,因为无限
for
循环被阻止了
然而,下面的代码并没有创建阻塞状态,测试成功。
正确,因为
for
循环运行的次数与您希望基准测试运行的次数一样多。一旦这个循环结束,你启动的 goroutine 就会被遗忘,你就有了泄漏。请参阅https://www.ardanlabs.com/blog/2018/11/goroutine-leaks-the-forgotten-sender.html:如果您启动一个 Goroutine,您希望最终终止但它永远不会终止,那么它有泄露了
在你的第二个例子中,goroutine 无限期地阻塞,这在技术上是一个泄漏但不会阻塞程序本身。
在您的第一个示例中,您无限期地循环在goroutine之外,因此程序将阻塞。这不是 Go 特有的,也与从频道接收没有真正的关系。你只是在没有退出条件的情况下循环,例如
while (true) {}
任何其他语言。
确保你没有在 goroutine 中无限循环以防止它泄漏。此外,一旦通道被清空并关闭,通道上的范围就会停止,因此您可以在完成填充后在 goroutine 中关闭它并改为范围:
func BenchmarkUnbufferedChannelInt(b *testing.B) {
ch := make(chan int)
go func() {
for i := 0; i < b.N; i++ {
ch <- i
}
close(ch)
}()
for _ := range ch {
}
}