我试图了解golang频道的工作原理。我读了一本关于go语言的书,并找到了以下示例。
package main
import (
"fmt"
)
// Send the sequence 2, 3, 4, ... to returned channel
func generate() chan int {
ch := make(chan int)
go func() {
for i := 2; i <= 100 ; i++ {
ch <- i
}
}()
return ch
}
// Filter out input values divisible by 'prime', send rest to returned channel
func filter(in chan int, prime int) chan int {
out := make(chan int)
go func() {
for {
if i := <-in; i%prime != 0 {
out <- i
}
}
}()
return out
}
func sieve() chan int {
out := make(chan int)
go func() {
ch := generate()
for {
prime := <-ch
ch = filter(ch, prime)
out <- prime
}
}()
return out
}
func main() {
primes := sieve()
for {
fmt.Println(<-primes)
}
}
当我运行这个程序时,我遇到了死锁,但当我将生成函数更改为
时// Send the sequence 2, 3, 4, ... to returned channel
func generate() chan int {
ch := make(chan int)
go func() {
for i := 2; ; i++ {
ch <- i
}
}()
return ch
}
然后程序将运行无限循环,但不会死锁。当我在for循环中删除条件时,为什么会出现死锁?
答案 0 :(得分:9)
你对阻止原则的意思是什么?
您可以在博文“The Nature Of Channels In Go ”
中看到它表示无缓冲频道:
(来自博客文章“The Nature Of Channels In Go ”的插图,由 William Kennedy 撰写,2014年2月)
无缓冲渠道没有容量,因此要求两个goroutine准备好进行任何交换。
当goroutine尝试将资源写入无缓冲通道并且没有goroutine等待接收资源时,通道将锁定goroutine并使其等待。
当goroutine尝试从无缓冲的频道中读取,并且没有goroutine等待发送资源时,该频道将锁定goroutine并使其等待。
这就是你的读者所遇到的情况:
func main() {
primes := sieve()
for {
fmt.Println(<-primes)
}
}
由于primes
永不关闭,main
仍然被屏蔽
它(main
)在第3步:
在步骤3中,右侧的goroutine将手放入通道或执行读取。 在交换完成之前,goroutine也被锁定在频道中。
发件人从不致电close(primes)
。
答案 1 :(得分:5)
让我们考虑一个更简单的例子:
func generate() chan int {
ch := make(chan int)
go func() {
for i := 2; /*i < 100*/; i++ {
ch <- i
}
}()
return ch
}
func main() {
for i := range generate() {
fmt.Println(i)
}
}
如果条件i < 100
取消注释,则generate
生成的goroutine会在发送98个号码后停止。但是,它不会关闭频道,因此main
无法知道将不再发送任何数字,并且它只是在频道上保持阻塞。由于main
现在仍然是唯一仍然存在的goroutine(另一个已经返回),并且它正在阻塞,所以你有一个僵局。