在你的代码中,出现死锁的原因是因为程序在等待信号的过程中没有正确地启动各个 goroutine,从而导致所有 goroutine 都无法继续执行。
具体来说,这里是你代码中的问题分析:
初始化通道:你首先向
channelCat
发送了一个值。这意味着channelCat
现在有一个值,并且任何尝试从channelCat
接收值的 goroutine 必须等待这个值被取走。Goroutines 的启动顺序:
- 第一个 goroutine 从
channelCat
中接收并打印 “cat”,然后向channelDog
发送信号。 - 第二个 goroutine 从
channelDog
中接收并打印 “dog”,然后向channelFish
发送信号。 - 第三个 goroutine 从
channelFish
中接收并打印 “fish”,然后再次向channelCat
发送信号。
- 第一个 goroutine 从
死锁情况:
- 当主函数创建并启动这些 goroutine 时,由于已经对
channelCat
写入了数据,第一个 goroutine 才能够执行并打印 “cat”。 - 然后它会尝试向
channelDog
写入数据,但此时还没有任何 goroutine 在等待接收来自channelDog
的数据,因此该操作将阻塞。 - 此时,第二个和第三个 goroutines 等待前一个通道的数据,而第一个通道也因阻塞而无法继续,导致程序死锁。
- 当主函数创建并启动这些 goroutine 时,由于已经对
如何修复
要解决这个问题,可以使用 buffered channel 来确保在通道满的时候不会导致阻塞。例如,你可以将通道声明为带缓冲区的:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
channelCat := make(chan struct{}, 1)
channelDog := make(chan struct{}, 1)
channelFish := make(chan struct{}, 1)
channelCat <- struct{}{}
wg := sync.WaitGroup{}
wg.Add(3)
go func() {
defer wg.Done()
<-channelCat
fmt.Println("cat")
time.Sleep(time.Second)
channelDog <- struct{}{}
}()
go func() {
defer wg.Done()
<-channelDog
fmt.Println("dog")
time.Sleep(time.Second)
channelFish <- struct{}{}
}()
go func() {
defer wg.Done()
<-channelFish
fmt.Println("fish")
time.Sleep(time.Second)
channelCat <- struct{}{}
}()
wg.Wait()
}
这里,通过将每个通道设为带缓冲区的(buffered),即使没有其他 goroutines 在同时读取,也不会造成阻塞。这样一来,就可以避免因通道未准备好而导致的死锁。
内容由零声教学AI助手提供,问题来源于学员提问