我正在研究Concurrency section的A Tour of Go,我对使用有限通道的Go约定感到好奇。在本练习中,我需要从两个通道读取值,并确定值是否相同且顺序相同。如果没有,我可以立即从我的方法返回false
。但是,如果我这样做,Go会自动为我清理我的频道,还是我的goroutines会永远悬空并消耗资源?
处理这个的最好方法是将一个取消通道传递给我的goroutines,但由于goroutines读取了有限数量的数据,所以只消耗所有数据似乎很好。在现实生活中处理这种情况的最佳方法是什么?
答案 0 :(得分:3)
Andrew Gerrand's talk at Gophercon在slide 37上涵盖了这个确切的问题。
创建一个退出频道并将其传递给每个助行器。通过关闭退出 当相同的退出时,任何正在运行的步行者都会被终止。
func Same(t1, t2 *tree.Tree) bool {
quit := make(chan struct{})
defer close(quit)
w1, w2 := Walk(t1, quit), Walk(t2, quit)
for {
v1, ok1 := <-w1
v2, ok2 := <-w2
if v1 != v2 || ok1 != ok2 {
return false
}
if !ok1 {
return true
}
}
}
答案 1 :(得分:2)
使用戒烟渠道, 正如在中讨论的那样 Go Concurrency Patterns: Pipelines and cancellation 博客文章 和Heath Borders&#39; answer, 通常是一个好主意。
还有
golang.org/x/net/context
package
在讨论中
Go Concurrency Patterns: Context
博客文章增加了超时,截止日期和其他功能。
然而,要直接解决:
将自动为我清理我的频道
这取决于通道缓冲以及通道的写入方式。
E.g。
func writeValues(n int, c chan int) {
for i := 0; i < n; i++ {
c <- i
}
log.Println("writeValues", n, "done")
}
func main() {
ch1 := make(chan int, 12)
ch2 := make(chan int, 6)
ch3 := make(chan int)
go writeValues(10, ch1)
go writeValues(11, ch2)
go writeValues(12, ch3)
time.Sleep(time.Second) // XXX
}
这里第一个goroutine将完成,ch1
(以及其中缓冲的任何内容)将被垃圾收集和清理。
然而,后来的两个goroutine将阻止等待,直到他们可以写出所有的值。垃圾收集器永远不会触及ch2
和ch3
,因为阻塞的goroutine会保留对它们的引用。
注意,如果从频道中读取的项目少至五个,则ch2
将清除。
通常,在执行以下操作时,您只能依赖于此:
errc := make(chan error, 1)
go func() { errc <- someErrorReturningFunc() }()
如果被调用的函数无法取消它,那么这是一个常见的习语。
您可以执行此操作并在不从errc
读取的情况下提前中止/返回,并且知道在函数最终返回时将清除goroutine和通道。
errc
频道的缓冲区大小非常重要。