[英]What happens when you break the for statement with a range channel
我正在按照此代码获取带有频道的惰性数字范围
// iterator
func iterator(n int, c chan int) {
for i := 0; i < n; i++ {
c <- i
}
close(c)
fmt.Println("iterator End")
}
c := make(chan int)
go iterator(5, c)
for i := range c {
fmt.Println(i)
}
这将按预期打印
0
1
2
3
4
fmt.Println("iterator End")
但是当我像这样打破for循环时发生了什么
c := make(chan int)
go getNumbers(5, c)
for i := range c {
if i == 2 {
break
}
fmt.Println(i)
}
似乎 goroutine 被阻塞了,因为从不打印iterator End
(我也尝试通过休眠主线程)。 我想知道如何处理这种情况? 我需要使用select
来解决这个问题吗? 有什么安全的方法可以检查范围是否中断并停止迭代器中的for 循环?
如果一个 goroutine 写入一个无缓冲的通道并且没有其他 goroutine 从该通道读取 - 那么写入将永远阻塞。 这将导致 goroutine 泄漏。 这就是你正在经历的。
如果你有一个写入通道的“生产者”goroutine,你需要一种方法来通知它停止。 关闭通道不是这里的关键部分 - 因为通道在 scope 中的 go 时被垃圾收集。阻塞的 goroutine(永远不会解除阻塞)被认为是泄漏,因为它永远不会被回收,所以你真的需要 goroutine 结束.
您可以通过多种方式表示退出的意图 - 最流行的两种方式是:
done
的频道; 要么func iterator(n int, c chan int, done <-chan struct{}) {
for i := 0; i < n; i++ {
select {
case c <- i:
case <-done:
break
}
}
close(c)
fmt.Println("iterator End")
}
读者协程:
c := make(chan int)
done := make(chan struct{})
go iterator(5, c, done)
for i := range c {
if i == 2 {
break
}
fmt.Println(i)
}
close(done) // signal writer goroutine to quit
func iterator(ctx context.Context, n int, c chan int) {
defer close(c)
defer fmt.Println("iterator End")
for i := 0; i < n; i++ {
select {
case c <- i:
case <-ctx.Done():
fmt.Println("canceled. Reason:", ctx.Err())
return
}
}
}
读取协程:
func run(ctx context.Context) {
ctx, cancel := context.WithCancel(ctx)
defer cancel() // call this regardless - avoid context leaks - but signals producer your intent to stop
c := make(chan int)
go iterator(ctx, 5, c)
for i := range c {
if i == 2 {
break
}
fmt.Println(i)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.