在你的代码中,死锁的原因主要有以下几点:
通道的同步问题:你在
for
循环中使用<-chaS[i]
来接收通道中的信号,这意味着每个 goroutine 都会等待前一个 goroutine 的信号。然而,在你的 goroutine 内部又向chaS[(i+1)/10]
发送了一个信号,该计算方式(i+1)/10
对于i=0
到i=9
的情况来说,总是会得到 0,从而使得在最后一次迭代后试图发送到chaS[1]
,这可能导致未初始化的 channel 被写入,最终造成死锁。并发控制不当:当你调用
<-chaS[i]
时,它阻塞等待直到有数据可读,而在 goroutine 中发送信号的逻辑可能不会被触发。此时,主循环没有正确地处理 goroutines 启动顺序,使得它们无法相互激活,从而导致死锁。不一致的索引:对于通道数组,你使用的是整除来确定下一个通道的索引
(i + 1) / 10
,这显然是不符合需求的,因为在大多数情况下都无法正确地引用到下一个通道索引。
下面是修复后的版本,可以帮助你避免这种死锁情况:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
chaS := make([]chan struct{}, 10)
for i := range chaS {
chaS[i] = make(chan struct{}) // 初始化每个通道
}
var global = 1
wg := sync.WaitGroup{}
wg.Add(10) // 提前设置待完成任务数
for i := 0; i < len(chaS); i++ {
go func(i int) {
defer wg.Done()
if i > 0 {
<-chaS[i-1] // 等待前一个 channel 信号
}
fmt.Println(global)
time.Sleep(time.Second)
global++
if i < len(chaS)-1 {
chaS[i] <- struct{}{} // 向下一个 channel 发送信号
}
}(i)
}
// 开始第一个 goroutine
chaS[0] <- struct{}{}
wg.Wait() // 等待所有 goroutines 完成
}
修复要点:
- 在每个 goroutine 中,我们检查是否存在前一个索引并进行接收,这样可以保证只有当前面结束时才继续。
- 使用增加全局 WaitGroup 的数量来确保我们等待所有协程完成。
- 修正了对下一个通道的引用逻辑,只需在确保不越界后进行发送。
这个修改确保了程序按照期望的顺序执行,并消除了潜在的死锁问题。
内容由零声教学AI助手提供,问题来源于学员提问