英文:
Why happen here a deadlock
问题
我正在尝试理解Golang中的通道工作原理。我读了一本关于Go语言的书,找到了以下示例。
package main
import (
"fmt"
)
// 将序列2、3、4、...发送到返回的通道
func generate() chan int {
ch := make(chan int)
go func() {
for i := 2; i <= 100; i++ {
ch <- i
}
}()
return ch
}
// 过滤掉能被'prime'整除的输入值,将剩余的值发送到返回的通道
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)
}
}
当我运行这个程序时,我遇到了死锁问题,但是当我将generate
函数更改为以下内容时:
// 将序列2、3、4、...发送到返回的通道
func generate() chan int {
ch := make(chan int)
go func() {
for i := 2; ; i++ {
ch <- i
}
}()
return ch
}
然后程序将进入无限循环,但不会发生死锁。为什么当我移除for
循环中的条件时会发生死锁呢?
英文:
I am trying to understand, how golang channel works. I read a book about the go language and found the following example.
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)
}
}
When I run this programm, I've got a deadlock, but when I change the generate function to
// 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
}
Then the programm will run the infinite loop, but not deadlock. Why do I get deadlock, when I remove the condition in for loop?
答案1
得分: 9
阻塞原则是指在使用无缓冲通道时,当一个goroutine尝试向通道写入资源但没有等待接收资源的goroutine时,通道会锁定该goroutine并使其等待;同样地,当一个goroutine尝试从无缓冲通道读取数据但没有等待发送资源的goroutine时,通道也会锁定该goroutine并使其等待。在你的代码中,由于primes
通道从未关闭,main
函数一直处于阻塞状态。这是因为在第三步中,右侧的goroutine将手放入通道或执行读取操作,该goroutine也会被锁定在通道中直到交换完成。发送方没有调用close(primes)
。
英文:
> What do you mean with blocking principle?
You can see it illustrated in the blog post "The Nature Of Channels In Go "
for an unbuffered channel:
<sup>(Illustration from blog post "The Nature Of Channels In Go ", written by William Kennedy, Feb. 2014)</sup>
> Unbuffered channels have no capacity and therefore require both goroutines to be ready to make any exchange.
When a goroutine attempts to write a resource to an unbuffered channel and there is no goroutine waiting to receive the resource, the channel will lock the goroutine and make it wait.
When a goroutine attempts to read from an unbuffered channel, and there is no goroutine waiting to send a resource, the channel will lock the goroutine and make it wait.
That is what happens in your case with your reader:
func main() {
primes := sieve()
for {
fmt.Println(<-primes)
}
}
since primes
is never closed, main
remains blocked.
It (main
) is in step 3:
> in step 3, the goroutine on the right places his hand into the channel or performs a read.
That goroutine is also locked in the channel until the exchange is complete.
The sender never calls close(primes)
.
答案2
得分: 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(另一个已经返回),而且它被阻塞了,所以发生了死锁。
英文:
Let's consider a simpler example:
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)
}
}
With the condition i < 100
uncommented, the goroutine spawned by generate
stops after sending 98 numbers. However, it does not close the channel, so main
has no way of knowing that no more numbers are going to be sent, and it just keeps blocking on the channel. Since main
is now the only goroutine still in existence (the other one has returned), and it's blocking, you have a deadlock.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论