不能复用单个done通道串起a→b→c,因b和c竞争同一信号,仅一个能接收并继续,另一个永久阻塞;须为每对依赖设独立通道(如ab、bc),并用chan error替代close()防panic漏信号。

用 channel 实现 goroutine 间硬依赖(如 A 必须完成,B 才能开始)最直接可靠,但必须避免复用通道、错判关闭时机、忽略 panic 传播。
为什么不能用单个 done 通道串起 A→B→C
多个 goroutine 接收同一个 done 通道时会竞争信号:B 和 C 都在 处阻塞,A <code>close(done) 后,只有一个能成功接收并继续,另一个永远卡住。这不是“顺序执行”,而是“竞态执行”。
正确做法是为每对依赖建独立通道:
- A 完成后 close(
ab),B 在开头等待 - B 完成后 close(
bc),C 在开头等待 - 通道类型推荐
chan struct{}——不传数据,只作信号
close() 调用方和时机怎么定才安全
只有发送方能调用 close(),且只能调一次;若 A 可能 panic,close() 就可能漏掉,导致 B 永久阻塞。
更健壮的写法是用 chan error:
- A 发送
err := doWork(); ch (不 close) - B 用
err, ok := 判断:ok 为 false 表示通道已关(A panic 或主动关),err 为非 nil 表示 A 执行失败 - 这样既防漏信号,又带错误上下文
多个前置任务全部完成才触发下游,该用 sync.WaitGroup 还是 channel
只关心“是否结束”,不用传结果,sync.WaitGroup 更轻量;但一旦需要消费返回值(比如 HTTP 响应体、解析后的结构体),就必须用 channel。
典型错误是混用:
- 用
WaitGroup等结束,再从全局变量读结果 → 并发不安全 - 用无缓冲
results chan Result,但没控制并发数 → 可能撑爆内存
推荐组合:WaitGroup 控制生命周期 + 带缓冲 results := make(chan Result, n) 收结果,主协程用 for i := 0; i 按序消费。
任一前置任务完成就启动下游,select 怎么写不 panic
每个前置任务配一个独立 resCh := make(chan Result, 1),启动 goroutine 后立刻 defer close(resCh),确保无论成功失败通道都关。
下游用 select 监听所有 resCh,收到第一个就 break;其余 goroutine 因通道已关,向 resCh 发送时会 panic —— 所以发送前加 select + default 非阻塞判断:
select {
case resCh <p>这比靠 <code>recover</code> 更干净,也避免了未消费 channel 的资源泄漏。</p><p>真正容易被忽略的是:依赖链里任意一环 panic,上游不会自动通知下游取消;必须显式用 <code>context.Context</code> 配合 <code>select</code> 把 cancel 信号透传下去,否则下游可能白等。</p>golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











