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

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

このコミットは、Go言語のメモリモデルに関する公式ドキュメント doc/go_mem.html を更新し、Go 1におけるプログラム初期化中に作成されるゴルーチンの振る舞いの変更を反映しています。具体的には、init 関数内で作成されたゴルーチンが、全ての init 関数の完了を待たずに並行して実行を開始できるようになった点が明記されています。

コミット

commit 6b770f05aded85c6e6eabeef498271cdf5df3c71
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Thu Mar 8 03:56:31 2012 +0800

    doc/go_mem: init-created goroutine behavior changes for Go 1
            They can start execution even before all init functions end.
    
    R=rsc, r, adg
    CC=golang-dev
    https://golang.org/cl/5732061

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

https://github.com/golang/go/commit/6b770f05aded85c6e6eabeef498271cdf5df3c71

元コミット内容

このコミットは、Go言語のメモリモデルに関するドキュメント doc/go_mem.html を修正しています。主な変更点は以下の通りです。

  1. ドキュメントのサブタイトルを「Version of June 10, 2011」から「Version of March 6, 2012」に更新。
  2. 「Initialization」セクションにおけるゴルーチンの振る舞いに関する記述を修正。
    • 以前の記述:「プログラムの初期化は単一のゴルーチンで実行され、初期化中に作成された新しいゴルーチンは、初期化が終了するまで実行を開始しない。」を削除。
    • 新しい記述:「プログラムの初期化は単一のゴルーチンで実行されるが、そのゴルーチンは他のゴルーチンを作成することができ、それらは並行して実行される。」を追加。
    • 以前のルール:「init 関数中に作成されたゴルーチンの実行は、全ての init 関数が終了した後に発生する。」を削除。

この変更は、Go 1のリリースに向けて、初期化時のゴルーチン実行に関するメモリモデルの保証が緩和されたことを反映しています。

変更の背景

Go言語は、並行処理を言語レベルでサポートするためにゴルーチンとチャネルを提供しています。Goのプログラムは、main パッケージの main 関数が実行される前に、パッケージの初期化と init 関数の実行という段階を経ます。

このコミットが行われた2012年3月は、Go言語の最初の安定版であるGo 1のリリースが間近に迫っていた時期です。Go 1の目標の一つは、言語仕様と標準ライブラリの安定化であり、その過程で既存の振る舞いや保証が再評価され、必要に応じて調整されました。

以前のGoのメモリモデルでは、プログラムの初期化フェーズ(特にinit関数の実行中)に作成されたゴルーチンは、全てのinit関数が完了するまで実行が開始されないという厳格な保証がありました。これは、初期化処理の予測可能性を高める一方で、初期化中に並行処理を必要とするシナリオにおいては、不必要な遅延や複雑さを生む可能性がありました。

このコミットは、この初期化時のゴルーチン実行に関する制約がGo 1で緩和されたことを反映しています。具体的には、init関数内で作成されたゴルーチンが、init関数の完了を待たずに並行して実行を開始できるようになりました。この変更は、初期化処理の柔軟性を高め、特定の初期化タスク(例: バックグラウンドでのリソースロード、非同期ログ初期化など)をより効率的に実行できるようにすることを目的としていると考えられます。

前提知識の解説

Go言語の初期化 (Initialization)

Go言語のプログラムは、実行開始時に特定の順序で初期化が行われます。

  1. パッケージの初期化: 各パッケージは、そのパッケージ内の変数の初期化式が評価され、その後、そのパッケージに定義されている init 関数が実行されます。
  2. init 関数: init 関数は、引数を取らず、戻り値も持たない特別な関数です。各パッケージは複数の init 関数を持つことができ、それらは定義された順序で実行されます。init 関数は、パッケージがインポートされた際に自動的に呼び出され、プログラムの起動時に一度だけ実行されます。主に、プログラムの起動時に必要な設定、リソースの初期化、登録処理などに使用されます。
  3. main 関数の実行: 全てのパッケージの初期化と init 関数の実行が完了した後、main パッケージの main 関数が実行されます。

ゴルーチン (Goroutine)

ゴルーチンは、Go言語における軽量な並行実行単位です。OSのスレッドよりもはるかに軽量であり、数千、数万のゴルーチンを同時に実行することが可能です。ゴルーチンは go キーワードを使って関数呼び出しの前に記述することで作成されます。

package main

import (
	"fmt"
	"time"
)

func sayHello() {
	fmt.Println("Hello from goroutine!")
}

func main() {
	go sayHello() // ゴルーチンを作成し、sayHelloを並行実行
	fmt.Println("Hello from main!")
	time.Sleep(10 * time.Millisecond) // ゴルーチンが実行されるのを待つ
}

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

Goメモリモデルは、Goプログラムにおけるメモリ操作の順序付けに関する保証を定義するものです。並行プログラムにおいて、複数のゴルーチンが共有メモリにアクセスする際に、どのような順序で操作が見えるかを規定します。これは、データ競合を防ぎ、プログラムの予測可能な振る舞いを保証するために非常に重要です。メモリモデルは、"happens before" 関係という概念を用いて、イベント間の順序を定義します。あるイベントAがイベントBの前に発生する場合、Aの効果はBから見えることが保証されます。

技術的詳細

このコミットの技術的な核心は、Goメモリモデルにおける「初期化フェーズ中のゴルーチン作成」に関する保証の変更です。

変更前(Go 1以前のドキュメントの記述):

  • 「プログラムの初期化は単一のゴルーチンで実行され、初期化中に作成された新しいゴルーチンは、初期化が終了するまで実行を開始しない。」
  • init 関数中に作成されたゴルーチンの実行は、全ての init 関数が終了した後に発生する。」

これは、init 関数内で go キーワードを使ってゴルーチンを作成しても、そのゴルーチン内のコードは、全ての init 関数(現在のパッケージだけでなく、インポートされた全てのパッケージの init 関数を含む)が完全に実行を終えるまで、一切実行されないことを意味していました。この厳格な順序付けは、初期化処理の予測可能性を非常に高く保つ一方で、初期化中に非同期処理を開始したい場合に、その処理が実際に開始されるまでに不必要な遅延が生じる可能性がありました。

変更後(Go 1以降のドキュメントの記述):

  • 「プログラムの初期化は単一のゴルーチンで実行されるが、そのゴルーチンは他のゴルーチンを作成することができ、それらは並行して実行される。」

この新しい記述は、init 関数内で作成されたゴルーチンが、init 関数の完了を待たずに「並行して実行される」ことを明確にしています。これは、初期化フェーズ中に作成されたゴルーチンが、init 関数がまだ実行中であっても、スケジューラによって実行が開始される可能性があることを意味します。

この変更の含意:

  1. 並行性の向上: 初期化処理中にバックグラウンドで非同期タスク(例: ネットワーク接続の確立、キャッシュのプリロード、ヘビーな計算など)を開始できるようになり、プログラム全体の起動時間を短縮できる可能性があります。
  2. 設計の柔軟性: init 関数内でより複雑な初期化ロジックを、並行処理を活用して設計できるようになります。
  3. 注意点: この変更により、init 関数内で作成されたゴルーチンが、init 関数がまだ完了していない状態で共有リソースにアクセスする可能性が生じます。したがって、開発者はデータ競合やデッドロックなどの並行処理の問題に、より注意を払う必要があります。特に、init 関数内で初期化中のグローバル変数や共有状態に、並行して実行されるゴルーチンがアクセスする場合、適切な同期メカニズム(ミューテックス、チャネルなど)を使用することが不可欠になります。

この変更は、Go 1のリリースにおける重要な決定の一つであり、Go言語が提供する並行処理の柔軟性を初期化フェーズにも拡張するものです。

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

変更は doc/go_mem.html ファイルに集中しており、Goメモリモデルのドキュメントの「Initialization」セクションが修正されています。

--- a/doc/go_mem.html
+++ b/doc/go_mem.html
@@ -1,6 +1,6 @@
 <!--{
  	"Title": "The Go Memory Model",
-	"Subtitle": "Version of June 10, 2011",
+	"Subtitle": "Version of March 6, 2012"
  	"Path": "/ref/mem"
  }-->
  
@@ -107,9 +107,9 @@ unspecified order.
  <h3>Initialization</h3>
  
  <p>
-Program initialization runs in a single goroutine and
-new goroutines created during initialization do not
-start running until initialization ends.
+Program initialization runs in a single goroutine,
+but that goroutine may create other goroutines,
+which run concurrently.
  </p>
  
  <p class="rule">
@@ -122,11 +122,6 @@ The start of the function <code>main.main</code> happens after
  all <code>init</code> functions have finished.
  </p>
  
-<p class="rule">
-The execution of any goroutines created during <code>init</code>
-functions happens after all <code>init</code> functions have finished.
-</p>
-
  <h3>Goroutine creation</h3>
  
  <p class="rule">

コアとなるコードの解説

このコミットは、Go言語のソースコード自体を変更するものではなく、Go言語の公式ドキュメントであるGoメモリモデルの記述を更新しています。したがって、「コアとなるコード」とは、Go言語のランタイムやコンパイラのコードではなく、このドキュメントの該当箇所を指します。

変更された箇所は、doc/go_mem.html 内の「Initialization」セクションです。

  1. サブタイトルの更新: "Subtitle": "Version of June 10, 2011""Subtitle": "Version of March 6, 2012" に変更されています。これは、ドキュメントの内容が更新され、新しい日付のバージョンとして公開されることを示しています。

  2. 初期化中のゴルーチンに関する記述の変更: 最も重要な変更は、以下の段落の修正です。

    • 削除された記述:

      <p>
      Program initialization runs in a single goroutine and
      new goroutines created during initialization do not
      start running until initialization ends.
      </p>
      

      この記述は、「初期化中に作成された新しいゴルーチンは、初期化が終了するまで実行を開始しない」という、以前の厳格なルールを明確に述べていました。

    • 追加された記述:

      <p>
      Program initialization runs in a single goroutine,
      but that goroutine may create other goroutines,
      which run concurrently.
      </p>
      

      この新しい記述は、「初期化中に作成されたゴルーチンが並行して実行される」ことを明示しています。これにより、init 関数がまだ実行中であっても、その中で起動されたゴルーチンが実行を開始する可能性があるという、Go 1での新しい振る舞いが反映されています。

  3. 初期化中のゴルーチン実行に関するルールの削除: 以下のルールが削除されました。

    <p class="rule">
    The execution of any goroutines created during <code>init</code>
    functions happens after all <code>init</code> functions have finished.
    </p>
    

    このルールは、init 関数内で作成されたゴルーチンが、全ての init 関数が完了するまで実行されないという、以前の保証を具体的に記述していました。このルールの削除は、上記の段落の変更と合わせて、この保証がGo 1で撤廃されたことを示しています。

これらの変更は、Go言語のメモリモデルが、初期化フェーズにおけるゴルーチンのスケジューリングに関して、より柔軟なアプローチを採用したことを開発者に伝えるためのものです。これにより、開発者は初期化処理において並行性をより積極的に活用できるようになりますが、同時に並行処理に伴う同期の問題に注意を払う必要性が生じます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント: https://go.dev/
  • Go言語のメモリモデルに関する議論や歴史的背景については、GoのメーリングリストやIssueトラッカー(例: golang.org/cl/5732061 に関連する議論)が参考になりますが、一般公開されているアーカイブから特定の議論を特定するのは困難な場合があります。
  • Go 1のリリースに関するブログ記事や技術解説記事(例: The Go Blogなど)も、当時の変更の背景を理解する上で役立ちます。

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

このコミットは、Go言語のメモリモデルに関する公式ドキュメント doc/go_mem.html を更新し、Go 1におけるプログラム初期化中に作成されるゴルーチンの振る舞いの変更を反映しています。具体的には、init 関数内で作成されたゴルーチンが、全ての init 関数の完了を待たずに並行して実行を開始できるようになった点が明記されています。

コミット

commit 6b770f05aded85c6e6eabeef498271cdf5df3c71
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Thu Mar 8 03:56:31 2012 +0800

    doc/go_mem: init-created goroutine behavior changes for Go 1
            They can start execution even before all init functions end.
    
    R=rsc, r, adg
    CC=golang-dev
    https://golang.org/cl/5732061

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

https://github.com/golang/go/commit/6b770f05aded85c6e6eabeef498271cdf5df3c71

元コミット内容

このコミットは、Go言語のメモリモデルに関するドキュメント doc/go_mem.html を修正しています。主な変更点は以下の通りです。

  1. ドキュメントのサブタイトルを「Version of June 10, 2011」から「Version of March 6, 2012」に更新。
  2. 「Initialization」セクションにおけるゴルーチンの振る舞いに関する記述を修正。
    • 以前の記述:「プログラムの初期化は単一のゴルーチンで実行され、初期化中に作成された新しいゴルーチンは、初期化が終了するまで実行を開始しない。」を削除。
    • 新しい記述:「プログラムの初期化は単一のゴルーチンで実行されるが、そのゴルーチンは他のゴルーチンを作成することができ、それらは並行して実行される。」を追加。
    • 以前のルール:「init 関数中に作成されたゴルーチンの実行は、全ての init 関数が終了した後に発生する。」を削除。

この変更は、Go 1のリリースに向けて、初期化時のゴルーチン実行に関するメモリモデルの保証が緩和されたことを反映しています。

変更の背景

Go言語は、並行処理を言語レベルでサポートするためにゴルーチンとチャネルを提供しています。Goのプログラムは、main パッケージの main 関数が実行される前に、パッケージの初期化と init 関数の実行という段階を経ます。

このコミットが行われた2012年3月は、Go言語の最初の安定版であるGo 1のリリースが間近に迫っていた時期です。Go 1の目標の一つは、言語仕様と標準ライブラリの安定化であり、その過程で既存の振る舞いや保証が再評価され、必要に応じて調整されました。

以前のGoのメモリモデルでは、プログラムの初期化フェーズ(特にinit関数の実行中)に作成されたゴルーチンは、全てのinit関数が完了するまで実行が開始されないという厳格な保証がありました。これは、初期化処理の予測可能性を高める一方で、初期化中に並行処理を必要とするシナリオにおいては、不必要な遅延や複雑さを生む可能性がありました。

このコミットは、この初期化時のゴルーチン実行に関する制約がGo 1で緩和されたことを反映しています。具体的には、init関数内で作成されたゴルーチンが、init関数の完了を待たずに並行して実行を開始できるようになりました。この変更は、初期化処理の柔軟性を高め、特定の初期化タスク(例: バックグラウンドでのリソースロード、非同期ログ初期化など)をより効率的に実行できるようにすることを目的としています。

前提知識の解説

Go言語の初期化 (Initialization)

Go言語のプログラムは、実行開始時に特定の順序で初期化が行われます。

  1. パッケージの初期化: 各パッケージは、そのパッケージ内の変数の初期化式が評価され、その後、そのパッケージに定義されている init 関数が実行されます。
  2. init 関数: init 関数は、引数を取らず、戻り値も持たない特別な関数です。各パッケージは複数の init 関数を持つことができ、それらは定義された順序で実行されます。init 関数は、パッケージがインポートされた際に自動的に呼び出され、プログラムの起動時に一度だけ実行されます。主に、プログラムの起動時に必要な設定、リソースの初期化、登録処理などに使用されます。
  3. main 関数の実行: 全てのパッケージの初期化と init 関数の実行が完了した後、main パッケージの main 関数が実行されます。

ゴルーチン (Goroutine)

ゴルーチンは、Go言語における軽量な並行実行単位です。OSのスレッドよりもはるかに軽量であり、数千、数万のゴルーチンを同時に実行することが可能です。ゴルーチンは go キーワードを使って関数呼び出しの前に記述することで作成されます。

package main

import (
	"fmt"
	"time"
)

func sayHello() {
	fmt.Println("Hello from goroutine!")
}

func main() {
	go sayHello() // ゴルーチンを作成し、sayHelloを並行実行
	fmt.Println("Hello from main!")
	time.Sleep(10 * time.Millisecond) // ゴルーチンが実行されるのを待つ
}

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

Goメモリモデルは、Goプログラムにおけるメモリ操作の順序付けに関する保証を定義するものです。並行プログラムにおいて、複数のゴルーチンが共有メモリにアクセスする際に、どのような順序で操作が見えるかを規定します。これは、データ競合を防ぎ、プログラムの予測可能な振る舞いを保証するために非常に重要です。メモリモデルは、"happens before" 関係という概念を用いて、イベント間の順序を定義します。あるイベントAがイベントBの前に発生する場合、Aの効果はBから見えることが保証されます。

技術的詳細

このコミットの技術的な核心は、Goメモリモデルにおける「初期化フェーズ中のゴルーチン作成」に関する保証の変更です。

変更前(Go 1以前のドキュメントの記述):

  • 「プログラムの初期化は単一のゴルーチンで実行され、初期化中に作成された新しいゴルーチンは、初期化が終了するまで実行を開始しない。」
  • init 関数中に作成されたゴルーチンの実行は、全ての init 関数が終了した後に発生する。」

これは、init 関数内で go キーワードを使ってゴルーチンを作成しても、そのゴルーチン内のコードは、全ての init 関数(現在のパッケージだけでなく、インポートされた全てのパッケージの init 関数を含む)が完全に実行を終えるまで、一切実行されないことを意味していました。この厳格な順序付けは、初期化処理の予測可能性を非常に高く保つ一方で、初期化中に非同期処理を開始したい場合に、その処理が実際に開始されるまでに不必要な遅延が生じる可能性がありました。

変更後(Go 1以降のドキュメントの記述):

  • 「プログラムの初期化は単一のゴルーチンで実行されるが、そのゴルーチンは他のゴルーチンを作成することができ、それらは並行して実行される。」

この新しい記述は、init 関数内で作成されたゴルーチンが、init 関数の完了を待たずに「並行して実行される」ことを明確にしています。これは、初期化フェーズ中に作成されたゴルーチンが、init 関数がまだ実行中であっても、スケジューラによって実行が開始される可能性があることを意味します。

この変更の含意:

  1. 並行性の向上: 初期化処理中にバックグラウンドで非同期タスク(例: ネットワーク接続の確立、キャッシュのプリロード、ヘビーな計算など)を開始できるようになり、プログラム全体の起動時間を短縮できる可能性があります。
  2. 設計の柔軟性: init 関数内でより複雑な初期化ロジックを、並行処理を活用して設計できるようになります。
  3. 注意点: この変更により、init 関数内で作成されたゴルーチンが、init 関数がまだ完了していない状態で共有リソースにアクセスする可能性が生じます。したがって、開発者はデータ競合やデッドロックなどの並行処理の問題に、より注意を払う必要があります。特に、init 関数内で初期化中のグローバル変数や共有状態に、並行して実行されるゴルーチンがアクセスする場合、適切な同期メカニズム(ミューテックス、チャネルなど)を使用することが不可欠になります。

この変更は、Go 1のリリースにおける重要な決定の一つであり、Go言語が提供する並行処理の柔軟性を初期化フェーズにも拡張するものです。

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

変更は doc/go_mem.html ファイルに集中しており、Goメモリモデルのドキュメントの「Initialization」セクションが修正されています。

--- a/doc/go_mem.html
+++ b/doc/go_mem.html
@@ -1,6 +1,6 @@
 <!--{
  	"Title": "The Go Memory Model",
-	"Subtitle": "Version of June 10, 2011",
+	"Subtitle": "Version of March 6, 2012"
  	"Path": "/ref/mem"
  }-->
  
@@ -107,9 +107,9 @@ unspecified order.
  <h3>Initialization</h3>
  
  <p>
-Program initialization runs in a single goroutine and
-new goroutines created during initialization do not
-start running until initialization ends.
+Program initialization runs in a single goroutine,
+but that goroutine may create other goroutines,
+which run concurrently.
  </p>
  
  <p class="rule">
@@ -122,11 +122,6 @@ The start of the function <code>main.main</code> happens after
  all <code>init</code> functions have finished.
  </p>
  
-<p class="rule">
-The execution of any goroutines created during <code>init</code>
-functions happens after all <code>init</code> functions have finished.
-</p>
-
  <h3>Goroutine creation</h3>
  
  <p class="rule">

コアとなるコードの解説

このコミットは、Go言語のソースコード自体を変更するものではなく、Go言語の公式ドキュメントであるGoメモリモデルの記述を更新しています。したがって、「コアとなるコード」とは、Go言語のランタイムやコンパイラのコードではなく、このドキュメントの該当箇所を指します。

変更された箇所は、doc/go_mem.html 内の「Initialization」セクションです。

  1. サブタイトルの更新: "Subtitle": "Version of June 10, 2011""Subtitle": "Version of March 6, 2012" に変更されています。これは、ドキュメントの内容が更新され、新しい日付のバージョンとして公開されることを示しています。

  2. 初期化中のゴルーチンに関する記述の変更: 最も重要な変更は、以下の段落の修正です。

    • 削除された記述:

      <p>
      Program initialization runs in a single goroutine and
      new goroutines created during initialization do not
      start running until initialization ends.
      </p>
      

      この記述は、「初期化中に作成された新しいゴルーチンは、初期化が終了するまで実行を開始しない」という、以前の厳格なルールを明確に述べていました。

    • 追加された記述:

      <p>
      Program initialization runs in a single goroutine,
      but that goroutine may create other goroutines,
      which run concurrently.
      </p>
      

      この新しい記述は、「初期化中に作成されたゴルーチンが並行して実行される」ことを明示しています。これにより、init 関数がまだ実行中であっても、その中で起動されたゴルーチンが実行を開始する可能性があるという、Go 1での新しい振る舞いが反映されています。

  3. 初期化中のゴルーチン実行に関するルールの削除: 以下のルールが削除されました。

    <p class="rule">
    The execution of any goroutines created during <code>init</code>
    functions happens after all <code>init</code> functions have finished.
    </p>
    

    このルールは、init 関数内で作成されたゴルーチンが、全ての init 関数が完了するまで実行されないという、以前の保証を具体的に記述していました。このルールの削除は、上記の段落の変更と合わせて、この保証がGo 1で撤廃されたことを示しています。

これらの変更は、Go言語のメモリモデルが、初期化フェーズにおけるゴルーチンのスケジューリングに関して、より柔軟なアプローチを採用したことを開発者に伝えるためのものです。これにより、開発者は初期化処理において並行性をより積極的に活用できるようになりますが、同時に並行処理に伴う同期の問題に注意を払う必要性が生じます。

関連リンク

参考にした情報源リンク