Golang中为什么会出现死锁问题
Golang中为什么会出现死锁问题 为什么在这种情况下第23行(<- done)会出现死锁
package main
func main() {
c := make(chan int)
done := make(chan bool)
go func() {
for i := 0; i < 50; i++ {
c <- i
}
done <- true
}()
go func() {
for i := 0; i < 50; i++ {
c <- i
}
done <- true
}()
<- done
<- done
close(c)
for i := range c {
println(i)
}
}
但在这种情况下没有死锁
package main
import "time"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 50; i++ {
<- c
}
}()
time.Sleep(time.Second * 5)
}
另外为什么当主线程睡眠时不会出现死锁?
更多关于Golang中为什么会出现死锁问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
4 回复
但在第二个示例中,虽然只进行写入操作而没有从通道读取,但它仍然正常工作
更多关于Golang中为什么会出现死锁问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
因为你没有在那里等待一个 done 通道。你的主 Go 协程在 5 秒后直接退出,并不关心其子协程的状态。
c 是无缓冲的,在第一个项目被读取之前不会接受第二个项目。由于你从未从 c 中读取数据,任何循环都无法成功完成,因此永远不会向 done 发送任何内容。
这是一个典型的Go语言并发编程中的死锁问题。让我详细解释死锁产生的原因和解决方案。
死锁原因分析
在第一个代码中,死锁发生在第23行 <- done,主要原因如下:
- 无缓冲通道的阻塞特性:
c和done都是无缓冲通道,发送和接收操作必须同步进行 - 发送操作被阻塞:两个goroutine都在向通道
c发送数据,但主goroutine在接收done信号之前没有从c接收数据 - 执行顺序问题:主goroutine先等待
done信号,但发送done信号的goroutine被阻塞在向c发送数据
具体执行流程:
// 问题代码的执行顺序
go func() { // goroutine1启动
for i := 0; i < 50; i++ {
c <- i // 阻塞!没有接收者
}
done <- true // 永远无法执行到这里
}()
<- done // 主goroutine阻塞等待,死锁!
修复方案
方案1:使用带缓冲的通道
package main
func main() {
c := make(chan int, 100) // 添加缓冲区
done := make(chan bool)
go func() {
for i := 0; i < 50; i++ {
c <- i
}
done <- true
}()
go func() {
for i := 0; i < 50; i++ {
c <- i
}
done <- true
}()
<-done
<-done
close(c)
for i := range c {
println(i)
}
}
方案2:分离发送和接收
package main
func main() {
c := make(chan int)
done := make(chan bool)
// 启动发送goroutine
go func() {
for i := 0; i < 50; i++ {
c <- i
}
done <- true
}()
go func() {
for i := 0; i < 50; i++ {
c <- i
}
done <- true
}()
// 启动接收goroutine
go func() {
<-done
<-done
close(c)
}()
for i := range c {
println(i)
}
}
方案3:使用select避免阻塞
package main
func main() {
c := make(chan int)
done := make(chan bool, 2) // 缓冲done通道
go func() {
for i := 0; i < 50; i++ {
select {
case c <- i:
default:
// 处理发送失败的情况
}
}
done <- true
}()
go func() {
for i := 0; i < 50; i++ {
select {
case c <- i:
default:
// 处理发送失败的情况
}
}
done <- true
}()
<-done
<-done
close(c)
for i := range c {
println(i)
}
}
关于第二个代码的问题
第二个代码没有死锁的原因是:
package main
import "time"
func main() {
c := make(chan int)
go func() {
for i := 0; i < 50; i++ {
<- c // 从空通道接收会阻塞,但goroutine不会退出
}
}()
time.Sleep(time.Second * 5) // 主goroutine睡眠,不执行其他操作
// 程序正常结束,所有goroutine被终止
}
这里没有死锁是因为:
- 接收goroutine在空通道上阻塞,但不会导致程序死锁
- 主goroutine睡眠5秒后程序直接退出
- Go运行时在程序退出时会终止所有goroutine
死锁检测只发生在所有goroutine都处于阻塞状态时。在这个例子中,主goroutine在睡眠期间不是阻塞状态(睡眠是可唤醒的等待),因此不会被判定为死锁。

