当前位置: > > > > 停止无限期写入通道的 goroutine
停止无限期写入通道的 goroutine
来源:stackoverflow
2024-04-22 08:18:33
0浏览
收藏
本篇文章给大家分享《停止无限期写入通道的 goroutine》,覆盖了Golang的常见基础知识,其实一个语言的全部知识点一篇文章是不可能说完的,但希望通过这些问题,让读者对自己的掌握程度有一定的认识(B 数),从而弥补自己的不足,更好的掌握它。
问题内容
我有一个函数可以创建一个无限期填充通道的 goroutine,例如:
func foo() <-chan int {
ch := make(chan int)
go func() {
defer close(ch)
for {
ch <- 1
}
}()
return ch
}
假设我们有一个消费者想在一段时间后停止:
ch:=foo() <-ch <-ch // done
现在我想清理 goroutine 资源,包括通道。我尝试为此添加一个“完成”通道,但随后我遇到了僵局:
func Foo() (<-chan int, chan<- bool) {
ch := make(chan int)
done := make(chan bool)
go func() {
defer close(ch)
for {
select {
case <-done:
return
default:
ch <- 1
}
}
}()
return ch, done
}
func main() {
ch, done := Foo()
<-ch
<-ch
done <- true
// HERE
}
现在,它似乎可以工作,但这只是因为程序退出,如果我用一些io操作替换// here(例如:http.get(“http://google.com”)) ,我面临死锁(fatal 错误:所有 goroutine 都在睡觉 - 死锁!)。 我想知道是否有另一种方法可以清理由 foo 函数创建的生成的 goroutine 和通道。
正确答案
只需在启动的 goroutine 中将 default 替换为 case 即可:
func Foo() (<-chan int, chan<- bool) {
ch := make(chan int)
done := make(chan bool)
go func() {
defer close(ch)
for {
select {
case <-done:
return
case ch <- 1:
}
}
}()
return ch, done
}
原代码在default情况下出现死锁的原因如下:
- 当没有其他并发运行的 goroutine 写入
done通道时,启动的写入ch通道的 goroutines 会立即转到default情况。然后 goroutine 会阻塞在ch <- 1行,直到其他 goroutine 从ch读取值。 - 主协程从
ch读取两次。这会导致在启动的 goroutine 处有两个成功的执行循环。然后它尝试写入done。此时启动的goroutine可能已经检查了select语句,陷入default情况并阻塞在ch <- 1行。因此主 goroutine 也会无限期地阻塞在done <- true行。这会导致僵局。
好了,本文到此结束,带大家了解了《停止无限期写入通道的 goroutine》,希望本文对你有所帮助!关注米云公众号,给大家分享更多Golang知识!
