Golang新手在这里。有人可以解释为什么以下代码会产生死锁吗?
我知道发送true到boolean< - 已完成的频道,但我不想使用它。
package main
import (
"fmt"
"sync"
"time"
)
var wg2 sync.WaitGroup
func producer2(c chan<- int) {
for i := 0; i < 5; i++ {
time.Sleep(time.Second * 10)
fmt.Println("Producer Writing to chan %d", i)
c <- i
}
}
func consumer2(c <-chan int) {
defer wg2.Done()
fmt.Println("Consumer Got value %d", <-c)
}
func main() {
c := make(chan int)
wg2.Add(5)
fmt.Println("Starting .... 1")
go producer2(c)
go consumer2(c)
fmt.Println("Starting .... 2")
wg2.Wait()
}
以下是我的理解,我知道这是错误的:
答案 0 :(得分:3)
你原来的死锁是由wg2.Add(5)
引起的,你等待5个goroutines完成,但只有一个做了;你打电话给wg2.Done()
一次。将其更改为wg2.Add(1)
,您的程序将正常运行。
但是,我怀疑你打算消耗频道中的所有值,而不是像你一样。如果您将消费者功能更改为:
func consumer2(c <-chan int) {
defer wg2.Done()
for i := range c {
fmt.Printf("Consumer Got value %d\n", i)
}
}
您将获得另一个死锁,因为生产者函数中的通道未关闭,并且消费者正在等待从未到达的更多值。将close(c)
添加到生成器函数将修复它。
答案 1 :(得分:1)
运行代码会出现以下错误:
➜ gochannel go run dl.go
Starting .... 1
Starting .... 2
Producer Writing to chan 0
Consumer Got value 0
Producer Writing to chan 1
fatal error: all goroutines are asleep - deadlock!
原因如下:
您的代码中有三个goroutine:main
,producer2
和consumer2
。当它运行时,
producer2
向频道0
consumer2
从频道中重温0
,退出 producer2
向频道发送1
,但没有人消费,因为consumer2
已退出producer2
等待 main
执行wg2.Wait()
,但并非所有waitgroup都已关闭。所以主要等待 两个goroutine在这里等着,什么都不做,无论你等多久都没有做任何事情。 这是一个僵局! Golang检测到它并发生恐慌。
这里有两个你困惑的概念:
我将在这里简要解释一下,互联网上有很多文章。
WaitGroup
如果有办法等待所有的灌浆完成。在后台运行goroutine时,重要的是要知道它们何时退出,然后才能执行某些操作。
在你的情况下,我们运行两个goroutine,所以在开始时我们应该设置wg2.Add(2)
,每个goroutine应该添加wg2.Done()
以通知它已完成。
从频道接收数据时。如果您确切知道它将发送多少数据,请以这种方式循环使用for
:
for i:=0; i<N; i++ {
data = <-c
process(data)
}
否则以这种方式使用它:
for data := range c {
process(data)
}
此外,当没有更多数据要发送时,别忘了关闭频道。
通过上述说明,代码可以固定为:
package main
import (
"fmt"
"sync"
"time"
)
var wg2 sync.WaitGroup
func producer2(c chan<- int) {
defer wg2.Done()
for i := 0; i < 5; i++ {
time.Sleep(time.Second * 1)
fmt.Printf("Producer Writing to chan %d\n", i)
c <- i
}
close(c)
}
func consumer2(c <-chan int) {
defer wg2.Done()
for i := range c {
fmt.Printf("Consumer Got value %d\n", i)
}
}
func main() {
c := make(chan int)
wg2.Add(2)
fmt.Println("Starting .... 1")
go producer2(c)
go consumer2(c)
fmt.Println("Starting .... 2")
wg2.Wait()
}
Here是解决此问题的另一种方法。
固定代码提供以下输出:
➜ gochannel go run dl.go
Starting .... 1
Starting .... 2
Producer Writing to chan 0
Consumer Got value 0
Producer Writing to chan 1
Consumer Got value 1
Producer Writing to chan 2
Consumer Got value 2
Producer Writing to chan 3
Consumer Got value 3
Producer Writing to chan 4
Consumer Got value 4