panic: 向已关闭的 channel 发送数据
panic: send on closed channel
ID: go/channel-send-on-closed-channel
版本兼容性
| 版本 | 状态 | 引入 | 弃用 | 备注 |
|---|---|---|---|---|
| 1.x | active | — | — | — |
根因分析
某个 goroutine 在另一个 goroutine 已调用 close(ch) 之后仍执行 ch <- v。运行时检测到向已关闭 channel 发送数据并 panic。常见于多个生产者共享一个 channel 且其中一个提前关闭,或 close 在所有发送者完成前被调用。
English
A goroutine calls ch <- v after another goroutine has already called close(ch). The runtime detects the send on a closed channel and panics. Common when multiple producers share a channel and one of them closes it, or when close is called before all senders finish.
解决方案
-
95% 成功率
Only the single owner (or a dedicated closer) closes the channel after all producers are done, using a sync.WaitGroup: var wg sync.WaitGroup for _, p := range producers { wg.Add(1); go func(p P){ defer wg.Done(); for v := range p { ch <- v } }(p) } go func(){ wg.Wait(); close(ch) }() -
88% 成功率
Use a separate done channel and select so senders bail out instead of sending after shutdown: select { case ch <- v: case <-done: return }
无效尝试
常见但无效的做法:
-
85% 失败
recover only works in the same goroutine that panicked and masks a genuine ownership bug; the channel stays closed so subsequent sends still panic.
-
90% 失败
Non-deterministic; under load the close can still race ahead of the send. Sleeps do not establish ordering.
-
80% 失败
Closing an already-closed channel panics with 'close of closed channel', trading one panic for another.