You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go运行报all goroutines are asleep死锁错误如何解决

Go通道死锁问题分析与修复

基础信息

  • 运行环境:go1.17.8 darwin/arm64,M1芯片Mac,macOS Monterey 12.4 (21F79)
  • 报错信息:fatal error: all goroutines are asleep - deadlock!
  • 代码预期目标:通过两个协程交替打印0-9的奇偶数列

死锁成因

代码通过两个无缓冲通道A、B实现协程同步:主协程发送启动信号到A,偶数协程读A后打印偶数、发送信号到B,奇数协程读B后打印奇数、回传信号到A,最终通过WaitGroup等待两个协程退出。
死锁触发逻辑在流程最后一步:

  1. 偶数协程总共执行5次循环(打印0/2/4/6/8),仅需要从A读取5次信号,完成最后一次打印(数值8)、向B发送信号后,就会退出循环执行wg.Done(),不会再读取A通道的信号。
  2. 奇数协程拿到B通道的最后一个信号后,打印最后一个奇数9,按照原有逻辑会向A写一个回执信号。但此时偶数协程已经退出,没有任何协程接收A通道的写入,奇数协程会直接阻塞在A <- true这行,永远无法执行wg.Done()。
  3. 此时WaitGroup的计数永远剩余1,主协程阻塞在wg.Wait(),所有协程都处于等待状态,直接触发死锁报错。

修复方案

核心是移除最后一次多余的、无对应接收方的通道写入:奇数协程打印完最后一个数字后,不需要再向A发送回执信号,直接退出循环即可。
修复后的完整可运行代码:

package main

import (
	"fmt"
	"sync"
)

func main() {
	A := make(chan bool)
	B := make(chan bool)
	defer close(A)
	defer close(B)
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		for i := 0; i < 10; i += 2 {
			if <-A {
				fmt.Println(i)
				B <- true
			}
		}
		wg.Done()
	}()

	go func() {
		for i := 1; i < 10; i += 2 {
			if <-B {
				fmt.Println(i)
				// 最后一次打印后无需回传信号,避免无接收方导致阻塞
				if i+2 < 10 {
					A <- true
				}
			}
		}
		wg.Done()
	}()

	A <- true
	wg.Wait()
}

运行后会按顺序输出0-9,无死锁问题。如果需要调整打印的数字上限,只需要修改两个循环的判断阈值,同时保证最后一个执行业务逻辑的协程不再回传通道信号即可。


内容的提问来源于stack exchange,提问作者Nemo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 08:46:07