为何Go程序报‘fatal error: all goroutines are asleep - deadlock!’死锁错误?
我是Go语言新手,在尝试典型的生产者消费者通道实验时,为做测试让生产者和消费者分别使用不同通道,代码如下:
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(2) fmt.Println("Starting the main application") channel := make(chan int) channel1 := make(chan int) go generateNumbersV2(&wg, channel) go printNumbersV2(&wg, channel1) fmt.Println("Waiting for other goroutines") wg.Wait() //close() //close(channel) fmt.Println("All goroutines finished") } func printNumbersV2(wg *sync.WaitGroup, rc <-chan int) { defer wg.Done() for idx := 0; idx < 3; idx++ { val := <-rc fmt.Println("******value received from channel ", val) } } func generateNumbersV2(wg *sync.WaitGroup, wc chan<- int) { defer wg.Done() for idx := 0; idx < 3; idx++ { wc <- idx fmt.Println("###value written to channel ", idx) } }
运行程序时出现错误:
fatal error: all goroutines are asleep - deadlock!
我理解当前两个goroutine分别因读取channel1和写入channel而阻塞,程序无法终止,但疑问是:若我确实在等待通道中的值,程序为何直接判定死锁而非无限等待?比如后续可能有其他goroutine向这些通道写入值的情况。
问题分析与解答
1. 代码阻塞的根源
你的代码核心问题是生产者和消费者用了完全无关的两个通道:
- 生产者
generateNumbersV2往channel发数据,但整个程序里没有任何goroutine从这个通道读,无缓冲通道的发送操作会一直阻塞,直到有接收者出现。 - 消费者
printNumbersV2从channel1读数据,但整个程序里没有任何goroutine往这个通道写,无缓冲通道的接收操作也会一直阻塞,直到有数据发送过来。
再加上主goroutine在wg.Wait()处阻塞,等待另外两个goroutine调用wg.Done(),三个goroutine全部陷入阻塞状态。
2. Go的死锁检测逻辑
Go运行时会主动监控所有goroutine的状态,当满足以下两个条件时,就会触发死锁错误终止程序:
- 所有goroutine都处于阻塞状态
- 不存在任何能解除阻塞的可能性(没有新goroutine会被创建,没有通道会有新的读写操作)
在你的场景里,运行时能明确判断:
- 主goroutine只会一直等
wg.Wait(),不会做任何其他操作 - 生产者goroutine的发送操作永远找不到接收者,无法推进
- 消费者goroutine的接收操作永远找不到发送者,无法推进
整个程序已经没有任何继续执行的可能,所以不会无限等待,而是直接抛出死锁错误。
如果是存在其他唤醒可能的场景——比如主goroutine在某个循环里会往通道写数据,或者会启动新的goroutine——那运行时就不会判定死锁,因为程序还有推进的可能。但你的代码里没有任何这样的路径。
3. 修复建议
最直接的修复是让生产者和消费者共用同一个通道,让发送和接收对应起来:
把go printNumbersV2(&wg, channel1)改成go printNumbersV2(&wg, channel)。
另外,为了避免消费者在生产者写完后继续阻塞,建议生产者完成发送后关闭通道,消费者用for range循环接收数据(这样能自动感知通道关闭),修改后的代码示例:
func printNumbersV2(wg *sync.WaitGroup, rc <-chan int) { defer wg.Done() // 用for range接收,通道关闭后自动退出循环 for val := range rc { fmt.Println("******value received from channel ", val) } } func generateNumbersV2(wg *sync.WaitGroup, wc chan<- int) { defer wg.Done() defer close(wc) // 发送完成后关闭通道 for idx := 0; idx < 3; idx++ { wc <- idx fmt.Println("###value written to channel ", idx) } }
内容的提问来源于stack exchange,提问作者TruckDriver

