Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 19501] ファイルの概要

このコミットは、Go言語のメモリモデルに関する公式ドキュメント doc/go_mem.html における、バッファ付きチャネルに関する happens-before ルールの記述ミスを修正するものです。既存の記述が2箇所で逆になっており、その誤りを訂正することで、Goメモリモデルの正確性を向上させています。

コミット

  • コミットハッシュ: 81a93ef24a502f74f542845b3a35f22a573f6876
  • 作者: Dmitriy Vyukov dvyukov@google.com
  • コミット日時: 2014年6月5日 木曜日 21:08:28 +0400

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/81a93ef24a502f74f542845b3a35f22a573f6876

元コミット内容

doc: fix happens-before rules for buffered channels
The current wording is reversed in 2 places.
Not sure how it got 4 LGTMs (mine was there as well).
Update #6242.

LGTM=dan.kortschak, r, rsc
R=golang-codereviews, 0xjnml, dan.kortschak, r, rsc
CC=golang-codereviews
https://golang.org/cl/101980047

変更の背景

Go言語のメモリモデルは、並行プログラムにおける操作の順序付けと可視性を定義する上で極めて重要です。特に、チャネルを介した通信は、Goにおける並行処理の主要な同期メカニズムであり、その happens-before ルールはプログラムの正確性を保証するために不可欠です。

このコミットの背景には、doc/go_mem.html に記載されていたバッファ付きチャネルに関する happens-before ルールが、実際には逆の順序で記述されていたという問題がありました。具体的には、「k番目の送信がk+C番目の受信の前に完了する」と書かれるべきところが、「k番目の受信がk+C番目の送信の前に完了する」と誤って記述されていました。この誤りは、Goのメモリモデルの理解に混乱を招き、誤った並行プログラムの設計につながる可能性がありました。

コミットメッセージにある #6242 は、この問題が報告されたGoのIssueトラッカーのIDです。このIssueを受けて、ドキュメントの記述が修正されることになりました。作者のDmitriy Vyukov氏自身も、以前のレビューでこの誤りを見落としていたことを認めており、ドキュメントの正確性を維持することの難しさと重要性を示しています。

前提知識の解説

Goメモリモデル (Go Memory Model)

Goメモリモデルは、Goプログラムにおけるメモリ操作(読み書き)の順序付けと、それらの操作が他のゴルーチンからどのように見えるか(可視性)を定義する一連のルールです。これは、並行プログラムにおいてデータ競合(data race)が発生しないようにし、プログラムの動作が予測可能であることを保証するために不可欠です。

並行プログラムでは、複数のゴルーチンが同時にメモリにアクセスする可能性があります。もしこれらのアクセスが適切に同期されない場合、プログラムの動作は非決定論的になり、デバッグが困難なバグ(データ競合)を引き起こす可能性があります。Goメモリモデルは、特定の同期プリミティブ(チャネル、ミューテックスなど)を使用することで、これらのメモリ操作が特定の順序で発生することを保証し、データ競合を回避する方法を提供します。

Happens-Before関係

Happens-Before関係は、並行処理におけるイベントの順序を定義する概念です。これは、物理的な時間順序ではなく、論理的な順序付けを指します。あるイベントAがイベントBの「前に発生する (happens before)」と定義される場合、イベントAの結果(例えば、Aによるメモリへの書き込み)は、イベントBから観測可能であることが保証されます。

Goメモリモデルにおけるhappens-before関係の主なルールには以下のようなものがあります。

  • 単一ゴルーチン内の順序: 単一のゴルーチン内では、プログラムの記述順序でイベントが発生します。
  • チャネル通信:
    • チャネルへの送信操作は、そのチャネルからの受信操作の前に発生します。
    • バッファなしチャネルの場合、送信が完了する前に受信が完了します。
    • バッファ付きチャネルの場合、k番目の送信は、k番目の受信が完了する前に発生します。
  • sync パッケージのプリミティブ: sync.Mutex のロック解除は、その後のロック取得の前に発生します。sync.WaitGroupAddWait の前に、DoneWait の前に発生します。
  • go ステートメント: go ステートメントによるゴルーチンの生成は、そのゴルーチンが実行を開始する前に発生します。
  • init 関数: パッケージの init 関数は、そのパッケージ内の任意の関数が実行される前に発生します。

happens-before関係は、コンパイラ最適化やCPUの命令再順序付けによって実際の実行順序が変更されたとしても、プログラマが期待する論理的な順序が保証されることを意味します。

バッファ付きチャネル (Buffered Channels)

バッファ付きチャネルは、内部に値を一時的に保持するための容量(バッファ)を持つチャネルです。make(chan Type, capacity) のように容量を指定して作成します。

  • 送信操作: バッファが満杯でない限り、送信操作はブロックされません。値はバッファに格納され、送信ゴルーチンはすぐに続行できます。バッファが満杯の場合、送信ゴルーチンはバッファに空きができるまでブロックされます。
  • 受信操作: バッファが空でない限り、受信操作はブロックされません。バッファから値が取り出され、受信ゴルーチンはすぐに続行できます。バッファが空の場合、受信ゴルーチンは値が送信されるまでブロックされます。

バッファ付きチャネルは、送信側と受信側の間の処理速度の差を吸収したり、セマフォのような並行制御メカニズムを実装したりするのに使用されます。

技術的詳細

このコミットは、doc/go_mem.html ファイル内の2つの主要な箇所を修正しています。

  1. バッファ付きチャネルの happens-before ルールの修正: 元の記述は以下のようになっていました。

    The <i>k</i>th send on a channel with capacity <i>C</i> happens before the <i>k</i>+<i>C</i>th receive from that channel completes.
    

    これは、「容量Cのチャネルへのk番目の送信は、そのチャネルからのk+C番目の受信が完了する前に発生する」という意味です。しかし、これは誤りでした。正しいhappens-before関係は、k番目の受信がk+C番目の送信の前に完了するというものです。

    修正後の記述は以下の通りです。

    The <i>k</i>th receive on a channel with capacity <i>C</i> happens before the <i>k</i>+<i>C</i>th send from that channel completes.
    

    この修正により、バッファ付きチャネルにおける送信と受信の間の正しいhappens-before関係が明確に定義されました。これは、チャネルのバッファが満杯になったときに送信がブロックされ、バッファが空になったときに受信がブロックされるというチャネルの動作と整合しています。つまり、バッファが満杯になるまで送信は進み、その後に受信がバッファを消費することで、さらに送信が可能になるという流れです。

  2. セマフォの比喩の修正: 元のドキュメントでは、バッファ付きチャネルをセマフォとしてモデル化する際の記述が、セマフォの一般的な使用法と逆になっていました。 元の記述:

    the number of items in the channel corresponds to the semaphore count,
    the capacity of the channel corresponds to the semaphore maximum,
    sending an item acquires the semaphore, and receiving an item releases
    the semaphore.
    This is a common idiom for rate-limiting work.
    

    この記述では、「アイテムの送信がセマフォの取得、アイテムの受信がセマフォの解放」とされていました。しかし、一般的なセマフォのモデルでは、セマフォの取得はリソースの消費を意味し、解放はリソースの利用可能化を意味します。バッファ付きチャネルをセマフォとして使う場合、チャネルへの送信はリソースの利用可能化(セマフォのカウント増加)に対応し、チャネルからの受信はリソースの消費(セマフォのカウント減少)に対応します。

    修正後の記述は以下の通りです。

    the number of items in the channel corresponds to the number of active uses,
    the capacity of the channel corresponds to the maximum number of simultaneous uses,
    sending an item acquires the semaphore, and receiving an item releases
    the semaphore.
    This is a common idiom for limiting concurrency.
    

    この修正では、セマフォの取得と解放の対応関係は維持しつつ、その比喩の表現がより正確になりました。特に、「rate-limiting work」から「limiting concurrency」への変更は、バッファ付きチャネルが並行処理の数を制限する用途でよく使われることをより適切に表現しています。

これらの修正は、Goメモリモデルのドキュメントの正確性を高め、Goの並行処理の理解を深める上で非常に重要です。

コアとなるコードの変更箇所

変更は doc/go_mem.html ファイルに対して行われました。

--- a/doc/go_mem.html
+++ b/doc/go_mem.html
@@ -1,6 +1,6 @@
 <!--{
  	"Title": "The Go Memory Model",
-	"Subtitle": "Version of March 6, 2012",
+	"Subtitle": "Version of May 31, 2014",
  	"Path": "/ref/mem"
 }-->
 
@@ -275,17 +275,17 @@ crash, or do something else.)
 </p>
 
 <p class="rule">
-The <i>k</i>th send on a channel with capacity <i>C</i> happens before the <i>k</i>+<i>C</i>th receive from that channel completes.
+The <i>k</i>th receive on a channel with capacity <i>C</i> happens before the <i>k</i>+<i>C</i>th send from that channel completes.
 </p>
 
 <p>
 This rule generalizes the previous rule to buffered channels.
 It allows a counting semaphore to be modeled by a buffered channel:
-the number of items in the channel corresponds to the semaphore count,
-the capacity of the channel corresponds to the semaphore maximum,
+the number of items in the channel corresponds to the number of active uses,
+the capacity of the channel corresponds to the maximum number of simultaneous uses,
 sending an item acquires the semaphore, and receiving an item releases
 the semaphore.
-This is a common idiom for rate-limiting work.
+This is a common idiom for limiting concurrency.
 </p>
 
 <p>

コアとなるコードの解説

上記の差分は、doc/go_mem.html 内の以下の2つの主要な変更を示しています。

  1. ドキュメントのバージョン日付の更新:

    -	"Subtitle": "Version of March 6, 2012",
    +	"Subtitle": "Version of May 31, 2014",
    

    これは、ドキュメントの内容が更新されたことを示すために、サブタイトル内の日付を2012年3月6日から2014年5月31日に変更したものです。これは、ドキュメントの改訂履歴を反映する標準的な慣行です。

  2. バッファ付きチャネルの happens-before ルールとセマフォの比喩の修正:

    -<p class="rule">
    -The <i>k</i>th send on a channel with capacity <i>C</i> happens before the <i>k</i>+<i>C</i>th receive from that channel completes.
    +<p class="rule">
    +The <i>k</i>th receive on a channel with capacity <i>C</i> happens before the <i>k</i>+<i>C</i>th send from that channel completes.
    </p>
    
    <p>
     This rule generalizes the previous rule to buffered channels.
     It allows a counting semaphore to be modeled by a buffered channel:
    -the number of items in the channel corresponds to the semaphore count,
    -the capacity of the channel corresponds to the semaphore maximum,
    +the number of items in the channel corresponds to the number of active uses,
    +the capacity of the channel corresponds to the maximum number of simultaneous uses,
     sending an item acquires the semaphore, and receiving an item releases
     the semaphore.
    -This is a common idiom for rate-limiting work.
    +This is a common idiom for limiting concurrency.
    </p>
    
    • happens-before ルールの修正: The <i>k</i>th send ... happens before the <i>k</i>+<i>C</i>th receive ... から The <i>k</i>th receive ... happens before the <i>k</i>+<i>C</i>th send ... へと変更されています。 これは、バッファ付きチャネルにおける送信と受信の間のhappens-before関係の記述を、より正確なものに修正したものです。具体的には、バッファが満杯になった後、k+C 番目の送信が完了するためには、k 番目の受信が完了してバッファに空きができる必要がある、という論理的な順序を反映しています。

    • セマフォの比喩の修正:

      • the number of items in the channel corresponds to the semaphore count,the number of items in the channel corresponds to the number of active uses, に変更されました。これは、チャネル内のアイテム数がセマフォの「カウント」という抽象的な概念よりも、「アクティブな使用数」という具体的な状態に対応することを示唆しています。
      • the capacity of the channel corresponds to the semaphore maximum,the capacity of the channel corresponds to the maximum number of simultaneous uses, に変更されました。これは、セマフォの「最大値」が「同時使用の最大数」に対応することをより明確にしています。
      • This is a common idiom for rate-limiting work.This is a common idiom for limiting concurrency. に変更されました。これは、バッファ付きチャネルをセマフォとして使用する主な目的が、処理の「レート制限」よりも「並行処理の制限」であることをより適切に表現しています。

これらの変更は、Goメモリモデルのドキュメントの正確性と理解しやすさを向上させる上で非常に重要です。

関連リンク

参考にした情報源リンク