
本文详解 sync.WaitGroup 未正常完成的典型原因——未关闭用于接收结果的 channel,导致 for range 永久阻塞;并提供完整、线程安全的并发请求处理范式。
本文详解 `sync.waitgroup` 未正常完成的典型原因——未关闭用于接收结果的 channel,导致 `for range` 永久阻塞;并提供完整、线程安全的并发请求处理范式。
在 Go 并发编程中,sync.WaitGroup 是协调 goroutine 生命周期的核心工具,但其行为高度依赖开发者对同步逻辑的精确控制。你提供的代码中,wg.Wait() 虽能正确等待所有 goroutine 完成,但后续的 for link := range result { ... } 却永远无法退出——根本原因在于:未关闭 result channel,致使 range 操作持续等待新值,形成永久阻塞。
Go 规范明确规定:for range 在 channel 上会一直阻塞,直到该 channel 被显式关闭(close(result))。而你的原始代码中,result 是一个无缓冲 channel,10 个 goroutine 向其发送数据后各自调用 wg.Add(-1)(⚠️注意:此处用法错误,应为 wg.Done()),但主 goroutine 在 wg.Wait() 返回后并未关闭 channel,导致 range 无限挂起。
此外,代码中还存在两个关键隐患:
-
wg.Add(-1)的误用:Add()接收负数虽合法,但语义混乱且易出错;应统一使用wg.Done()(等价于wg.Add(-1))并在 defer 中调用,确保即使发生 panic 也能正确计数。 - 未处理并发写入竞争:多个 goroutine 同时向同一 channel 发送数据是安全的,但若后续需聚合结果(如存入 slice),则需额外同步机制。
✅ 正确做法如下(已修复全部问题):
getUrl










