我试图了解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)
}
}
Run Code Online (Sandbox Code Playgroud)
当我运行这个程序时,我遇到了死锁,但是当我将生成函数更改为
// 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
}
Run Code Online (Sandbox Code Playgroud)
然后程序将运行无限循环,但不会死锁.当我在for循环中删除条件时,为什么会出现死锁?
阻塞原则是什么意思?
你可以看到它在博客中说明" 频道的性质转到 "
对于无缓冲的频道:
(来自博客文章" 走进频道的本质 " 的插图,由William Kennedy撰写,2014年2月)
无缓冲通道没有容量,因此需要两个goroutine准备进行任何交换.
当goroutine尝试将资源写入无缓冲通道并且没有goroutine等待接收资源时,通道将锁定goroutine并使其等待.
当goroutine尝试从无缓冲的通道读取,并且没有goroutine等待发送资源时,通道将锁定goroutine并使其等待.
这就是你的读者在你的情况下发生的事情:
func main() {
primes := sieve()
for {
fmt.Println(<-primes)
}
}
Run Code Online (Sandbox Code Playgroud)
因为primes
从未关闭,main
仍然被阻止.
它(main
)在第3步:
在步骤3中,右侧的goroutine将手放入通道或执行读取.
在交换完成之前,goroutine也被锁定在频道中.
发件人从不打电话close(primes)
.
让我们考虑一个更简单的例子:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
在条件未i < 100
注释的情况下,goroutine generate
在发送98个数字后停止产生.但是,它不会关闭频道,因此main
无法知道将不再发送任何数字,并且它只是在频道上保持阻塞.既然main
现在是唯一仍然存在的goroutine(另一个已经返回),并且它正在阻塞,那么你就陷入了僵局.