for-loop - 当您使用范围通道中断 for 语句时会发生什么
问题描述
我正在按照此代码获取带有频道的惰性数字范围
// 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,你需要一种方法来通知它停止。关闭通道不是这里的关键部分 - 因为通道超出范围时会被垃圾收集。阻塞的 goroutine(永远不会解除阻塞)被认为是泄漏,因为它永远不会被回收,所以你真的需要 goroutine 结束。
您可以通过多种方式表示退出意图 - 最受欢迎的两种方式是:
- 一个
done
频道;或者 - context.Context取消
信号:完成通道
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")
}
阅读器 goroutine:
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
信号:context.Context
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
}
}
}
阅读 goroutine:
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)
}
}
推荐阅读
- c# - C# Webhook InitializeReceiveGenericJsonWebHooks
- oracle - 是否可以在 Oracle/SQL 中使用对象作为 UDF 的参数?
- swift - 将 Realm List 转换为 Array 而不显式其类型
- netlogo - Netlogo 蒙德里安绘画生成器
- angular - mat-autocomplete 没有正确绑定
- python - 将两个变量添加到 QComboBox
- javascript - 在Node js中直接将chart.js图表创建为PNG?
- woocommerce - 为 woo 产品设置 date_created
- php - 命名空间和使用之间的区别,尤其是在 php laravel 框架中
- java - java.lang.NoSuchMethodError: com.mongodb.ConnectionString.getRetryWritesValue()Ljava/lang/Boolean