在下面的示例中,go 例程将值泵入无缓冲通道,并且 main 函数对其进行迭代。
package main
import (
"fmt"
"strconv"
)
var chanStr chan string
func main() {
go pump()
fmt.Println("iterating ...")
for val := range chanStr {
fmt.Printf("fetched val: %s from channel\n", val)
}
}
func pump() {
defer close(chanStr)
chanStr = make(chan string)
for i := 1; i <= 5; i++ {
fmt.Printf("pumping seq %d into channel\n", i)
chanStr <- "val" + strconv.Itoa(i)
}
//close(chanStr)
}
该函数会出现恐慌并输出以下内容:
iterating ...
pumping seq 1 into channel
pumping seq 2 into channel
fetched val: val1 from channel
......
fetched val: val4 from channel
pumping seq 5 into channel
panic: close of nil channel
goroutine 5 [running]:
main.pump()
C:/personal/gospace/go-rules/test.go:26 +0x1a6
created by main.main
C:/personal/gospace/go-rules/test.go:11 +0x4e
但是,如果我评论 defer 语句并在 goroutine 中的 for 循环之后立即关闭pump,接收器不会惊慌。 这两种情况有什么区别?看起来 defer 在收到值之前关闭通道,但常规关闭会等待。
此外,当我使用竞争检测器进行构建时,即使在常规关闭中,它也会标记潜在的竞争条件(我无法每次都重新创建竞争)。这是否意味着这两种方式都不能优雅地关闭通道?
更新: 对于所有评论,我知道问题是什么。我必须在函数的第一行创建通道main()。不过,我在 Windows 上运行 go1.12,并且观察到了这种行为。显然我没有伪造输出。我一直使用 defer 语句重新创建恐慌,甚至在 for 循环后立即关闭通道时也没有发生过恐慌pump()
梵蒂冈之花
手掌心
慕标5832272
相关分类