send on closed channel パニックの原因と直し方
close()で閉じた後のチャネルに値を送信しようとすると、実行時にパニックが発生します。チャネルを閉じるのは送信側の役割で、閉じた後は一切送信できません。
エラーメッセージの読み方
panic: send on closed channel
panic- パニック — 実行時に回復不能な状態になったことを示します。deferとrecoverで捕まえない限り、そのままプロセスが終了します
send on closed channel- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 package main 2 3 func main() { 4 ch := make(chan int, 1) 5 close(ch) 6 ch <- 1 ^ 7 }
panic: send on closed channel
closeした後のチャネルに送信しようとすると即座にパニックします。closeは「もう送信しない」という宣言だからです。
直し方: ch <- 1 を _ = ch にします。
広告
広告スロット(未設定)
パターン2
1 package main 2 3 func main() { 4 ch := make(chan string, 1) 5 close(ch) 6 ch <- "go" ^ 7 }
panic: send on closed channel
バッファに空きがあっても、closeされていれば送信は受け付けられずパニックします。
直し方: ch <- "go" を _ = ch にします。
パターン3
1 package main 2 3 func main() { 4 ch := make(chan bool, 2) 5 close(ch) 6 ch <- true ^ 7 }
panic: send on closed channel
複数goroutineが同じチャネルに送信する設計では、誰が閉じる責任を持つかを明確にしないとこの事故が起きます。
直し方: ch <- true を _ = ch にします。
パターン4
1 package main 2 3 func main() { 4 ch := make(chan int, 3) 5 close(ch) 6 ch <- 10 ^ 7 }
panic: send on closed channel
バッファの容量に関わらず、closeされたチャネルへの送信は常にパニックの対象です。
直し方: ch <- 10 を _ = ch にします。
パターン5
1 package main 2 3 func main() { 4 ch := make(chan float64, 1) 5 close(ch) 6 ch <- 1.5 ^ 7 }
panic: send on closed channel
受信側は閉じたチャネルからゼロ値を読み取れますが、送信側にはそのような救済はありません。
直し方: ch <- 1.5 を _ = ch にします。
よくある誤解
「チャネルを閉じても、まだ値を送りたければ送れるはず」という思い込みは誤りです。closeは「もう送信しない」という宣言であり、閉じた後の送信は即座にパニックとして扱われます。
まとめ
send on closed channel パニックの原因と直し方は中級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。