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

为何这些Goroutine的WaitGroup无法正常工作?如何避免死锁?

解决Go语言WaitGroup死锁的关键细节

兄弟,我看你这是在Go里用WaitGroup的时候踩了死锁的坑,太常见了!我帮你捋捋最容易忽略的几个点,你对照自己的代码排查下:

首先得明确WaitGroup的核心逻辑:必须在启动Goroutine前调用wg.Add(n),每个子Goroutine结束时必须调用wg.Done(),最后在主Goroutine里用wg.Wait()等待所有子任务完成。你大概率是在这几个环节漏了细节:

  • Add调用时机太晚
    要是你在启动Goroutine之后才调用wg.Add(1),甚至把Add放在子Goroutine里,主Goroutine可能已经先跑到wg.Wait()了。这时候WaitGroup计数是0,Wait会直接返回(导致子Goroutine还在跑主程序就退出),或者主Goroutine会一直等计数增加,直接死锁。
    错误写法示例:

    var wg sync.WaitGroup
    go func() {
        wg.Add(1) // 错!Add在子Goroutine里,主Goroutine可能已经开始Wait了
        defer wg.Done()
        // 业务逻辑
    }()
    wg.Wait()
    

    正确姿势:启动前先Add

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        // 业务逻辑
    }()
    wg.Wait()
    
  • Done调用不完整
    你的Read...函数如果有分支逻辑(比如错误处理、提前return),很可能某些路径下没调用wg.Done(),导致WaitGroup的计数永远减不到0,主Goroutine就会一直卡着Wait,直接死锁。
    解决办法超简单:把wg.Done()用defer放在子Goroutine的最开头,不管函数怎么退出(正常结束、报错return),都会执行Done:

    wg.Add(1)
    go func() {
        defer wg.Done() // 放在这里,稳得一批
        reader := bufio.NewReader(os.Stdin)
        str, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("读取错误:", err)
            return // 这里return也会触发defer的Done
        }
        fmt.Println("读取内容:", str)
    }()
    
  • Wait的位置不对
    要是你把wg.Wait()放到了某个子Goroutine里,或者在循环启动Goroutine的时候逻辑乱了(比如Wait之后又启动新的Goroutine),也会出问题。Wait必须放在所有子Goroutine都启动完成之后,而且得在主Goroutine里执行。

最后给你个完整的正确示例,结合了接口和并发,你可以参考调整自己的代码:

package main

import (
	"bufio"
	"fmt"
	"os"
	"sync"
)

// 定义Reader接口
type Reader interface {
	Read() string
}

// 实现StdinReader
type StdinReader struct{}

func (sr StdinReader) Read() string {
	reader := bufio.NewReader(os.Stdin)
	str, _ := reader.ReadString('\n')
	return str
}

func main() {
	var wg sync.WaitGroup
	reader := StdinReader{}

	// 启动3个读取Goroutine
	for i := 0; i < 3; i++ {
		wg.Add(1) // 启动前先加计数
		go func(id int) {
			defer wg.Done() // 确保退出时执行Done
			content := reader.Read()
			fmt.Printf("Goroutine %d 读取到内容: %s", id, content)
		}(i)
	}

	wg.Wait() // 等待所有子Goroutine完成
	fmt.Println("\n所有读取任务都完成啦!")
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:50:33