0

0

如何正确使用 Go 语言中的 sync.WaitGroup 避免程序永不退出

心靈之曲

心靈之曲

发布时间:2026-01-07 23:17:02

|

845人浏览过

|

来源于php中文网

原创

如何正确使用 Go 语言中的 sync.WaitGroup 避免程序永不退出

本文详解 `sync.waitgroup` 常见误用导致程序卡在 `wg.wait()` 不返回的问题,重点说明值传递 vs 指针传递、`defer wg.done()` 的调用时机等关键陷阱,并提供可立即修复的代码示例。

在 Go 并发编程中,sync.WaitGroup 是协调多个 goroutine 完成等待的经典工具。但若使用不当,极易引发程序“假死”——看似所有任务已完成,wg.Wait() 却永不返回。你提供的代码正是典型反例,问题根源在于两个关键错误:

? 错误一:WaitGroup 被值传递(copy),导致 Done() 失效

函数 downloadFromURL(url string, wg sync.WaitGroup) 的第二个参数是 值类型,Go 会复制整个 WaitGroup 结构体传入。后续在 goroutine 中调用 wg.Done(),实际操作的是副本,对 main 中原始 wg 的计数器 零影响。因此 wg.Wait() 永远等待未完成的 goroutine。

✅ 正确做法:必须传递指针

go downloadFromURL(url, &wg) // 传地址

并同步更新函数签名:

OpenJobs AI
OpenJobs AI

AI驱动的职位搜索推荐平台

下载
func downloadFromURL(url string, wg *sync.WaitGroup) error {
    // ...
}

? 错误二:defer wg.Done() 放置位置不当

当前代码中 defer wg.Done() 写在函数末尾(return nil 之前),看似合理,实则危险:一旦函数因错误提前 return(如文件创建失败、HTTP 请求异常),defer 将被跳过,Done() 永不执行,WaitGroup 计数器无法归零。

✅ 正确做法:defer wg.Done() 应置于函数最开始
它应是 goroutine 启动后立即注册的“收尾承诺”,确保无论函数以何种路径退出,计数器必减一:

func downloadFromURL(url string, wg *sync.WaitGroup) error {
    defer wg.Done() // ✅ 第一行就声明:我结束时必调用 Done()

    tokens := strings.Split(url, "/")
    fileName := tokens[len(tokens)-1]
    fmt.Printf("Downloading %v to %v \n", url, fileName)

    content, err := os.Create("temp_docs/" + fileName)
    if err != nil {
        fmt.Printf("Error while creating %v because of %v\n", fileName, err)
        return err // 此处 return → defer wg.Done() 仍会执行
    }
    defer content.Close() // 别忘了关闭文件!

    resp, err := http.Get(url)
    if err != nil {
        fmt.Printf("Could not fetch %v because %v\n", url, err)
        return err // 同样,defer wg.Done() 保证执行
    }
    defer resp.Body.Close()

    _, err = io.Copy(content, resp.Body)
    if err != nil {
        fmt.Printf("Error while saving %v from %v\n", fileName, url)
        return err
    }

    fmt.Printf("Download complete for %v \n", fileName)
    return nil
}

⚠️ 其他重要注意事项

  • wg.Add(1) 必须在 go 语句前调用:你的代码已正确(wg.Add(1) 在 go downloadFromURL(...) 之前),这是安全的;若放在 goroutine 内部,则存在竞态风险。
  • 避免重复 Add/Over-add:确保每个 go 启动的 goroutine 对应且仅对应一次 Add(1)。
  • WaitGroup 不可复制或重用:一旦 Wait() 返回,该 WaitGroup 实例不应再被 Add() 或再次 Wait();如需复用,请重新声明变量。
  • 调试技巧:sync.WaitGroup 本身不提供公开 API 查询当前计数,但可通过 unsafe 或 reflect(不推荐生产环境)窥探;更实用的方法是添加日志:在 Add(1) 和 Done() 处打印计数变化,或使用 go tool trace 分析 goroutine 生命周期。

修复后的完整 main 函数逻辑清晰、健壮可靠:

func main() {
    links := parseLinks()
    var wg sync.WaitGroup

    for _, url := range links {
        if isExcelDocument(url) {
            wg.Add(1)
            go downloadFromURL(url, &wg) // ✅ 传指针
        } else {
            fmt.Printf("Skipping: %v\n", url)
        }
    }
    wg.Wait() // ✅ 现在能正确返回
    fmt.Println("All downloads completed.")
}

遵循这两条铁律——指针传递 WaitGroup + defer Done() 置顶——即可彻底规避 WaitGroup 永不完成的陷阱,写出稳定、可维护的并发 Go 程序。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

194

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

186

2025.07.04

java值传递和引用传递有什么区别
java值传递和引用传递有什么区别

java值传递和引用传递的区别:1、基本数据类型的传递;2、对象的传递;3、修改引用指向的情况。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

106

2024.02.23

http500解决方法
http500解决方法

http500解决方法有检查服务器日志、检查代码错误、检查服务器配置、检查文件和目录权限、检查资源不足、更新软件版本、重启服务器或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

307

2023.11.09

http请求415错误怎么解决
http请求415错误怎么解决

解决方法:1、检查请求头中的Content-Type;2、检查请求体中的数据格式;3、使用适当的编码格式;4、使用适当的请求方法;5、检查服务器端的支持情况。更多http请求415错误怎么解决的相关内容,可以阅读下面的文章。

396

2023.11.14

HTTP 503错误解决方法
HTTP 503错误解决方法

HTTP 503错误表示服务器暂时无法处理请求。想了解更多http错误代码的相关内容,可以阅读本专题下面的文章。

1436

2024.03.12

http与https有哪些区别
http与https有哪些区别

http与https的区别:1、协议安全性;2、连接方式;3、证书管理;4、连接状态;5、端口号;6、资源消耗;7、兼容性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1859

2024.08.16

java学习网站推荐汇总
java学习网站推荐汇总

本专题整合了java学习网站相关内容,阅读专题下面的文章了解更多详细内容。

6

2026.01.08

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Excel 教程
Excel 教程

共162课时 | 11.1万人学习

成为PHP架构师-自制PHP框架
成为PHP架构师-自制PHP框架

共28课时 | 2.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号