Golang中主协程读取通道时遇到所有协程休眠的致命错误

Golang中主协程读取通道时遇到所有协程休眠的致命错误 运行以下代码时出现以下错误。

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]: main.main()

链接:https://go.dev/play/p/2zey2TNZmv2

注意:我是在主 goroutine 上从通道读取时遇到此错误。如果我使用单独的 goroutine 读取,则运行正常。我想知道为什么在主线程上无法工作。

2 回复

通道从未被关闭,因此 for range 会等待第四个元素。

我怀疑当你使用单独的 goroutine 时它能正常工作,它只是看起来在工作,因为主函数在等待 wg,并在那里获得一个同步点,尽管接收的 goroutine 仍在无限期地等待数据。

更多关于Golang中主协程读取通道时遇到所有协程休眠的致命错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个典型的Go死锁问题。当主协程(goroutine 1)尝试从通道接收数据时,程序中没有其他活跃的协程来发送数据,导致所有协程都处于休眠状态。

问题分析

在你的代码中:

package main

func main() {
    ch := make(chan int)
    
    // 这里主协程会阻塞等待接收数据
    <-ch  // fatal error: all goroutines are asleep - deadlock!
}

为什么会产生死锁

  1. 主协程阻塞在通道接收操作<-ch 会一直等待直到有数据发送到通道
  2. 没有其他活跃协程:程序中没有启动其他协程来发送数据
  3. Go运行时检测到死锁:当所有协程都处于等待状态(没有可运行的协程),Go运行时就会抛出这个错误

解决方案对比

方案1:使用单独的goroutine发送数据(你的代码应该采用的方式)

package main

func main() {
    ch := make(chan int)
    
    go func() {
        ch <- 42  // 在另一个协程中发送数据
    }()
    
    // 主协程接收数据
    value := <-ch
    println(value)  // 输出: 42
}

方案2:使用缓冲通道(临时解决方案)

package main

func main() {
    ch := make(chan int, 1)  // 创建缓冲大小为1的通道
    
    ch <- 42  // 不会阻塞,因为通道有缓冲区
    
    value := <-ch
    println(value)  // 输出: 42
}

方案3:先发送后接收(在同一个协程中)

package main

func main() {
    ch := make(chan int)
    
    // 必须使用goroutine,否则会死锁
    go func() {
        ch <- 42
    }()
    
    // 主协程等待接收
    println(<-ch)  // 输出: 42
}

核心原则

在Go中,无缓冲通道的发送和接收操作是同步的

  • 发送操作会阻塞,直到有另一个协程执行接收操作
  • 接收操作会阻塞,直到有另一个协程执行发送操作

当你在主协程中执行接收操作时,必须确保有其他协程(非主协程)会执行发送操作,否则就会发生死锁。

这就是为什么“使用单独的goroutine读取可以正常工作”的原因——读取操作在另一个协程中,主协程可以继续执行发送操作,两者可以互相配合完成通信。

回到顶部