[インデックス 11717] ファイルの概要
このコミットは、Go言語の仕様書 doc/go_spec.html
を更新し、クローズされたチャネルへの送信がどのように振る舞うかを明確に定義しています。具体的には、クローズされたチャネルへの送信が「進行する (proceeding)」と見なされ、その結果としてランタイムパニックを引き起こすことを明記しています。これにより、select
ステートメントの動作記述を簡素化し、仕様の曖昧さを解消しています。
コミット
commit e7a138b856b21bc3db8ddb1dfe9f3fabe0dc59e8
Author: Russ Cox <rsc@golang.org>
Date: Wed Feb 8 15:24:48 2012 -0500
spec: send on closed channel counts as "proceeding"
Other wordings are possible but defining this as one
of the ways to proceed means we don't have to add
language about this to the description of select.
Fixes #2825.
R=golang-dev, gri, r
CC=golang-dev
https://golang.org/cl/5643062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e7a138b856b21bc3db8ddb1dfe9f3fabe0dc59e8
元コミット内容
spec: send on closed channel counts as "proceeding"
Other wordings are possible but defining this as one
of the ways to proceed means we don't have to add
language about this to the description of select.
Fixes #2825.
R=golang-dev, gri, r
CC=golang-dev
https://golang.org/cl/5643062
変更の背景
Go言語のチャネルは、ゴルーチン間の安全な通信を可能にする強力な並行処理プリミティブです。しかし、チャネルがクローズされた後にそのチャネルに値を送信しようとした場合の正確な振る舞いについて、初期のGo言語仕様では明確な記述が不足していました。
この曖昧さは、特に select
ステートメント内でクローズされたチャネルへの送信操作がどのように扱われるかという点で問題を引き起こす可能性がありました。select
ステートメントは、複数の通信操作の中から準備ができたものを選択するメカニズムですが、チャネルがクローズされた場合の送信操作が「準備ができた」と見なされるのか、それともブロックするのか、あるいは別の結果になるのかが不明確でした。
このコミットの目的は、この曖昧さを解消し、クローズされたチャネルへの送信がランタイムパニックを引き起こすという既存の実装の振る舞いを仕様として明文化することです。これにより、select
ステートメントの記述を簡素化し、言語のセマンティクスをより明確にすることが可能になりました。コミットメッセージにある「Other wordings are possible but defining this as one of the ways to proceed means we don't have to add language about this to the description of select.」という記述は、この変更が select
の複雑な記述を避けるための設計上の選択であることを示唆しています。
前提知識の解説
Go言語のチャネル (Channels)
Go言語のチャネルは、ゴルーチン間で値を送受信するための通信メカニズムです。チャネルは make(chan Type)
で作成され、Type
はチャネルが送受信する要素の型です。
- 送信 (Send):
ch <- value
の形式でチャネルに値を送信します。 - 受信 (Receive):
value := <-ch
または<-ch
の形式でチャネルから値を受信します。
チャネルには以下の2種類があります。
- バッファなしチャネル (Unbuffered Channels):
make(chan Type)
で作成されます。送信操作は受信操作が準備できるまでブロックし、受信操作は送信操作が準備できるまでブロックします。つまり、送信と受信が同時に行われる必要があります。 - バッファありチャネル (Buffered Channels):
make(chan Type, capacity)
で作成されます。capacity
はチャネルが保持できる要素の最大数です。バッファが満杯でない限り、送信操作はブロックしません。バッファが空でない限り、受信操作はブロックしません。
チャネルのクローズ (Closing Channels)
チャネルは close(ch)
関数を使ってクローズすることができます。チャネルをクローズすると、それ以上そのチャネルに値を送信することはできません。しかし、クローズされたチャネルからは、バッファに残っている値があればそれらを受信できます。バッファが空になった後、クローズされたチャネルから受信しようとすると、その型のゼロ値と、チャネルがクローズされたことを示す false
のブール値が返されます(例: value, ok := <-ch
)。
ランタイムパニック (Run-time Panics)
Go言語におけるパニックは、プログラムの通常の実行フローを中断させるエラーの一種です。パニックは、通常、回復不可能なエラー(例: nilポインタのデリファレンス、配列の範囲外アクセス)が発生した場合に発生します。パニックが発生すると、現在のゴルーチンの実行が停止し、遅延関数 (deferred functions) が実行された後、プログラム全体がクラッシュするか、recover
関数によってパニックが捕捉されない限り、スタックトレースが出力されます。
select
ステートメント
select
ステートメントは、複数のチャネル操作を待機し、そのうちのいずれかが準備できたときに実行されるGo言語の制御構造です。select
は switch
ステートメントに似ていますが、case
がチャネルの送受信操作である点が異なります。
select
の case
は、以下のいずれかの条件が満たされたときに「準備ができた (ready)」と見なされます。
- 送信操作: チャネルが送信を受け入れる準備ができている(バッファに空きがある、または受信側が準備できている)。
- 受信操作: チャネルから値を受信する準備ができている(チャネルに値がある、またはチャネルがクローズされており、バッファが空になっている)。
複数の case
が同時に準備できた場合、select
はランダムに1つを選択して実行します。どの case
も準備できていない場合、default
case
があればそれが実行されます。default
case
がなく、どの case
も準備できていない場合は、いずれかの case
が準備できるまで select
ステートメントはブロックします。
技術的詳細
このコミットが導入する最も重要な技術的変更は、Go言語の仕様において「クローズされたチャネルへの送信」の振る舞いを明確に定義した点です。以前は、この状況がどのように扱われるかについて、仕様書には明示的な記述がありませんでした。しかし、Go言語のランタイムは、実際にはクローズされたチャネルへの送信を常にランタイムパニックとして処理していました。このコミットは、この既存の振る舞いを正式に仕様に組み込むものです。
具体的には、チャネルへの送信操作が「進行する (proceeding)」条件の一つとして、「クローズされたチャネルへの送信がランタイムパニックを引き起こすことによって進行する」という記述が追加されました。
この変更の技術的な意味合いは以下の通りです。
- 明確なセマンティクス: クローズされたチャネルへの送信は、もはや未定義の振る舞いではなく、明確に定義されたエラー条件(パニック)となります。これにより、開発者はこの状況を予測し、適切にコードを設計できるようになります。
select
ステートメントの簡素化:select
ステートメントは、そのcase
が「進行できる」かどうかを評価します。このコミット以前は、クローズされたチャネルへの送信がselect
のcase
内でどのように扱われるかについて、特別な考慮が必要でした。しかし、この変更により、クローズされたチャネルへの送信も「進行する」操作(ただしパニックを伴う)として扱われるため、select
のロジックを複雑にすることなく、このケースを自然に組み込むことができます。つまり、select
は、クローズされたチャネルへの送信case
が「準備ができた」と判断し、それを実行しようとしますが、その結果としてパニックが発生します。これにより、select
の内部実装や仕様記述において、クローズされたチャネルへの送信を特別扱いする必要がなくなります。- 一貫性: この変更は、Go言語の他の部分におけるエラー処理(特に回復不可能なエラーに対するパニックの使用)との一貫性を保ちます。チャネルがクローズされた後に送信を試みることは、通常、プログラマの論理的な誤りを示すものであり、パニックはこのような誤りを早期に発見するのに役立ちます。
このコミットは、Go言語の安定性と予測可能性を高める上で重要な役割を果たしています。
コアとなるコードの変更箇所
変更は doc/go_spec.html
ファイルのチャネル送信に関するセクションで行われています。
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -3687,10 +3687,10 @@ Channel = Expression .
<p>
Both the channel and the value expression are evaluated before communication
-begins. Communication blocks until the send can proceed, at which point the
-value is transmitted on the channel.
+begins. Communication blocks until the send can proceed.
A send on an unbuffered channel can proceed if a receiver is ready.
A send on a buffered channel can proceed if there is room in the buffer.
+A send on a closed channel proceeds by causing a <a href="#Run_time_panics">run-time panic</a>.
A send on a <code>nil</code> channel blocks forever.
</p>
コアとなるコードの解説
このコミットでは、doc/go_spec.html
内のチャネル送信に関する段落が修正されています。
変更前:
Both the channel and the value expression are evaluated before communication
begins. Communication blocks until the send can proceed, at which point the
value is transmitted on the channel.
A send on an unbuffered channel can proceed if a receiver is ready.
A send on a buffered channel can proceed if there is room in the buffer.
A send on a <code>nil</code> channel blocks forever.
この記述では、「送信が進行できるまで通信はブロックする」と述べられていますが、クローズされたチャネルへの送信が「進行できる」条件に含まれるかどうかが不明確でした。
変更後:
Both the channel and the value expression are evaluated before communication
begins. Communication blocks until the send can proceed.
A send on an unbuffered channel can proceed if a receiver is ready.
A send on a buffered channel can proceed if there is room in the buffer.
A send on a closed channel proceeds by causing a <a href="#Run_time_panics">run-time panic</a>.
A send on a <code>nil</code> channel blocks forever.
変更点とそれぞれの意味は以下の通りです。
-
- value is transmitted on the channel.
の削除:- これは、送信が進行した結果として値がチャネルに送信されるという、自明な部分を削除したものです。クローズされたチャネルへの送信は値が送信されないため、この文言は不適切になるため削除されました。
-
+ A send on a closed channel proceeds by causing a <a href="#Run_time_panics">run-time panic</a>.
の追加:- これがこのコミットの核心です。クローズされたチャネルへの送信が「進行する」条件の一つとして明示的に追加されました。ただし、その進行はランタイムパニックを引き起こすという結果を伴います。
<a href="#Run_time_panics">run-time panic</a>
のリンクは、Go言語仕様書内のランタイムパニックのセクションへの内部リンクであり、読者がパニックについてさらに詳しく学ぶことができるように配慮されています。
この変更により、Go言語のチャネル送信のセマンティクスがより厳密かつ明確になり、特にクローズされたチャネルへの送信というエッジケースの振る舞いが公式に定義されました。
関連リンク
- Go言語の公式ドキュメント - Channels: https://go.dev/tour/concurrency/2
- Go言語の公式ドキュメント - Select: https://go.dev/tour/concurrency/5
- Go言語の公式ドキュメント - Panics: https://go.dev/blog/defer-panic-and-recover
注記: コミットメッセージに記載されている Fixes #2825
の元のGitHub Issue 2825は、現在のGoリポジトリのIssueトラッカーでは直接見つけることができませんでした。これは、GoプロジェクトのIssueトラッカーが時間とともに移行されたり、Issue番号が再利用されたりする可能性があるためです。また、https://golang.org/cl/5643062
のChange List (CL) リンクも、現在のGerritシステムでは直接アクセスできませんでした。これは、古いCL番号がアーカイブされたか、URL構造が変更されたためと考えられます。しかし、コミットメッセージと差分から、変更の意図と内容を十分に理解できます。
参考にした情報源リンク
- Go言語の公式ドキュメント (Go Programming Language Specification)
- Go言語の公式ブログ (The Go Blog)
- Go言語のソースコードリポジトリ (GitHub: golang/go)
- Go言語のチャネル、パニック、
select
ステートメントに関する一般的な知識