我正在阅读“Go 中的并发”,并发现了这个 goroutine 泄漏的示例:
func main() {
var wg sync.WaitGroup
doWork := func(strings <-chan string) <-chan interface{} {
completed := make(chan interface{})
go func() {
defer fmt.Println("doWork exited.")
defer close(completed)
defer wg.Done()
fmt.Println("a")
for s := range strings {
fmt.Println(s)
}
fmt.Println("b")
}()
return completed
}
wg.Add(1)
doWork(nil)
fmt.Println("Waiting")
wg.Wait()
fmt.Println("Done.")
}
The
strings
channel will never gets any strings written onto it, and the goroutine containingdoWork
will remain in memory for the life time of process.
我不明白 - 为什么?
我如何理解这段代码:
由于
strings
是nil
range
- 循环刚刚被跳过。对于任何超过nil
的范围:slice := []int{10, 20, 30, 40, 50} slice = nil for i := range slice { fmt.Println(i) } fmt.Println("Done")
fmt.Println("doWork exited.")
将被执行close(completed)
将被执行
但我发现它是这样工作的。为什么?
最佳答案
As strings is nil range-loop just skipped.
这个假设是不正确的。在 Go 中,从 nil
channel 读取总是会阻塞。这是在 language specification 中定义的(感谢@peterSO 挖掘出链接):
Receiving from a nil channel blocks forever.
还有a post on the Go Design Patterns blog进一步阐述了这种行为并强调了它有用的一些情况。
无论如何,这种行为可以通过一个最小的示例轻松重现( playground ):
func main() {
var s chan string
<- s
}
这个程序永远不会完成(在 Playground 上,它会崩溃,所有 goroutine 都在 sleep - 死锁
)。
因为从 nil channel 读取(在您的示例中,字符串
)会阻塞(永远,因为没有任何内容可以写入 nil channel ),doWork
goroutine 永远不会完成,因此会泄漏。
关于go - 为什么这个 goroutine 会泄漏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57239639/