Code Fix

中級

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は「もう送信しない」という宣言であり、閉じた後の送信は即座にパニックとして扱われます。

実務での勘所

チャネルを閉じる権利が送信側だけにあるのは、複数の受信側が存在しうる状況を考えると理にかなっています。もし受信側が自由にチャネルを閉じられたら、複数の受信側のうち誰か1人が閉じた瞬間、まだ動いている送信側や他の受信側の処理が壊れてしまいます。送信側だけが「もうこれ以上値を生成しない」と確実に判断できる立場にあるため、閉じる責任も送信側に一本化されています。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)