在您提供的代码中,确实会导致死锁。让我们逐步分析一下原因。
原因分析
全局变量
global
的并发访问:global
是一个共享变量,在多个 goroutine 中被同时读取和写入,而没有进行同步控制。这可能导致数据竞争,并且不保证在所有 goroutine 中都能正确更新global
的值。通道操作的顺序问题:每个 goroutine 在尝试从其相应的 channel (
channelS[global]
) 中读取之前,需要等待其他 goroutine 完成自己的工作。但是,由于没有适当地处理global
的更新,某些 goroutine 可能会永远阻塞。例如,如果global
达到 n(例如 5),它将尝试访问channelS[5]
,但该索引超出了切片的范围,从而引发错误。启动顺序不当:程序逻辑是通过
channelS[0] <- struct{}{}
开始第一个 goroutine,但因为没有确保其他 goroutine 能够按照正确的顺序执行,所以可能会出现死锁。
修改建议
为了避免这些问题,可以使用如下修改:
- 使用互斥锁来保护对
global
变量的访问。 - 确保不会越界访问 channel 切片。
- 确保每个协程可以在合适的时候退出,不至于造成死锁。
以下是经过修改后的示例代码:
package main
import (
"fmt"
"sync"
)
func main() {
n := 5
channelS := make([]chan struct{}, n)
for i := 0; i < n; i++ {
channelS[i] = make(chan struct{})
}
var wg sync.WaitGroup
wg.Add(n)
global := 0
var mu sync.Mutex // 创建互斥锁
for i := 0; i < n; i++ {
go func(i int) {
defer wg.Done()
if i == 0 {
channelS[0] <- struct{}{} // 启动第一个通道
}
<-channelS[i]
mu.Lock() // 加锁以安全地读取和写入 global
fmt.Println(i) // 打印当前索引而不是 global
global++
if global < n { // 确保不会越界
channelS[global] <- struct{}{}
}
mu.Unlock() // 解锁
}(i)
}
wg.Wait()
}
更正后的关键点
- 使用了一个互斥锁 (
sync.Mutex
) 来保护对global
的访问,确保它在线程间安全。 - 修改了打印逻辑,以便每个线程能够打印自己的索引而不是使用共享变量。
- 添加了条件检查,以确保不会尝试发送到超出通道范围的索引。
内容由零声教学AI助手提供,问题来源于学员提问