[インデックス 16852] ファイルの概要
このコミットは、Go言語の math/rand
パッケージにおけるデフォルトの乱数生成器 (Source
) が、複数のゴルーチンによる同時利用に対してスレッドセーフであることを明記するためのドキュメントの追加です。これにより、開発者が math/rand
パッケージのトップレベル関数(rand.Int
や rand.Float64
など)を安心して並行処理で使用できることが明確になります。
コミット
- コミットハッシュ:
e97c870692aa80feabd2e22c191476220fc1281f
- Author: Andrew Gerrand adg@golang.org
- Date: Wed Jul 24 08:27:20 2013 +1000
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e97c870692aa80feabd2e22c191476220fc1281f
元コミット内容
math/rand: mention that the default Source is thread-safe
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/11709043
変更の背景
math/rand
パッケージは、擬似乱数を生成するための機能を提供します。このパッケージには、rand.Int()
や rand.Float64()
のようなトップレベル関数があり、これらは内部的に共有のデフォルト Source
を使用しています。Go言語は並行処理を容易にするゴルーチンをサポートしているため、複数のゴルーチンが同時にこれらの乱数生成関数を呼び出すシナリオが頻繁に発生します。
乱数生成器は通常、内部状態を持っており、その状態が複数のスレッド(またはゴルーチン)から同時に変更されると、競合状態(race condition)が発生し、予測不能な結果や誤った乱数シーケンスが生成される可能性があります。このような問題を防ぐためには、乱数生成器がスレッドセーフである必要があります。
このコミット以前は、math/rand
パッケージのドキュメントには、デフォルトの Source
がスレッドセーフであるかどうかが明示されていませんでした。これにより、開発者は並行環境でトップレベル関数を使用する際に、明示的な同期メカニズム(ミューテックスなど)を追加する必要があるのか、それともGoランタイムが既に安全性を保証しているのかについて疑問を抱く可能性がありました。このコミットは、その曖昧さを解消し、デフォルトの Source
が並行利用に対して安全であることを明確にすることで、開発者の混乱を防ぎ、コードの信頼性を向上させることを目的としています。
前提知識の解説
1. 擬似乱数生成 (Pseudo-Random Number Generation, PRNG)
コンピュータで生成される乱数は、実際には真の乱数ではなく、特定のアルゴリズムと初期値(シード)に基づいて計算される「擬似乱数」です。同じシードを与えれば、常に同じ乱数シーケンスが生成されます。math/rand
パッケージはこの擬似乱数生成を提供します。
2. math/rand
パッケージ
Go言語の標準ライブラリの一部であり、一般的な用途(シミュレーション、ゲームなど)向けの擬似乱数生成機能を提供します。
- トップレベル関数:
rand.Int()
,rand.Float64()
など、パッケージ名で直接呼び出せる関数。これらは内部で共有のデフォルトSource
を使用します。 Source
インターフェース: 乱数生成のアルゴリズムと状態を定義するインターフェース。Int63()
メソッドを持ち、63ビットの符号なし整数を生成します。Rand
型:Source
インターフェースをラップし、より多様な乱数生成メソッド(Intn
,Float32
など)を提供する構造体。rand.New(source)
で特定のSource
を持つRand
インスタンスを作成できます。- シード (Seed): 乱数生成器の初期状態を設定するための値。
rand.Seed(value)
でデフォルトのSource
のシードを設定できます。同じシードからは同じ乱数シーケンスが生成されます。
3. スレッドセーフティと並行処理
- スレッドセーフ (Thread-safe): 複数のスレッド(またはゴルーチン)から同時にアクセスされても、データ競合や不正な状態に陥ることなく、正しく動作する性質を指します。
- ゴルーチン (Goroutine): Go言語における軽量な並行実行単位。OSのスレッドよりもはるかに軽量で、数千、数万のゴルーチンを同時に実行することが可能です。
- 競合状態 (Race Condition): 複数のゴルーチンが共有リソースに同時にアクセスし、少なくとも1つが書き込み操作を行う場合に、実行順序によって結果が異なる現象。これはプログラムのバグの一般的な原因となります。
- ミューテックス (Mutex): Mutual Exclusion(相互排他)の略。共有リソースへのアクセスを同期するためのメカニズム。ミューテックスがロックされている間は、他のゴルーチンはそのリソースにアクセスできません。これにより、一度に一つのゴルーチンだけが共有リソースを変更できるようになり、競合状態を防ぎます。
技術的詳細
math/rand
パッケージのデフォルトの Source
は、内部的に sync.Mutex
を使用して保護されています。これにより、rand.Int()
や rand.Float64()
のようなトップレベル関数が複数のゴルーチンから同時に呼び出された場合でも、乱数生成器の内部状態へのアクセスが同期され、データ競合が発生しないようになっています。
具体的には、デフォルトの Source
は lockedSource
という非公開の型で実装されており、この型は sync.Mutex
を埋め込んでいます。乱数生成メソッドが呼び出されるたびに、このミューテックスがロックされ、乱数生成処理が完了した後にアンロックされます。これにより、一度に一つのゴルーチンだけが乱数生成器の状態を更新できるため、スレッドセーフが保証されます。
しかし、このスレッドセーフティは、rand.NewSource()
で独自に作成した Source
インスタンスや、rand.New(rand.NewSource(...))
で作成した rand.Rand
インスタンスには適用されません。これらのユーザーが明示的に作成した乱数生成器は、デフォルトではスレッドセーフではないため、複数のゴルーチンで共有して使用する場合には、開発者自身が sync.Mutex
などの同期メカニズムを実装する必要があります。
また、math/rand
は擬似乱数生成器であり、暗号学的に安全な乱数が必要な場合には適していません。セキュリティが重要な用途(パスワード生成、鍵生成など)では、crypto/rand
パッケージを使用する必要があります。crypto/rand
パッケージはOSが提供するエントロピー源を利用するため、デフォルトでスレッドセーフであり、かつ予測不可能な真の乱数に近い値を提供します。
Go 1.20で導入された math/rand/v2
では、デフォルトのグローバルジェネレータのスケーラビリティが改善され、多くの場合、グローバルミューテックスの必要性がなくなりました。これは、スレッドごとの wyrand
ジェネレータを使用することで実現されています。ただし、rand.Seed
が明示的に呼び出された場合、ミューテックスで保護されたGo 1のジェネレータに戻る点には注意が必要です。
このコミットは、既存の math/rand
パッケージの動作を変更するものではなく、その動作に関するドキュメントを改善するものです。
コアとなるコードの変更箇所
--- a/src/pkg/math/rand/rand.go
+++ b/src/pkg/math/rand/rand.go
@@ -8,6 +8,7 @@
// Float64 and Int, use a default shared Source that produces a deterministic
// sequence of values each time a program is run. Use the Seed function to
// initialize the default Source if different behavior is required for each run.
+// The default Source is safe for concurrent use by multiple goroutines.
package rand
import "sync"
コアとなるコードの解説
変更は src/pkg/math/rand/rand.go
ファイルのコメントに1行追加されただけです。
追加された行:
// The default Source is safe for concurrent use by multiple goroutines.
この1行は、math/rand
パッケージの冒頭にあるパッケージコメントに追加されました。このコメントは、パッケージの目的と基本的な使用方法を説明するものです。この変更により、パッケージのユーザーは、rand.Int()
や rand.Float64()
のようなトップレベル関数が内部で使用するデフォルトの乱数生成器が、複数のゴルーチンから同時に呼び出されても安全であることが明確に理解できるようになりました。
これは、コードの動作自体を変更するものではなく、既存の動作(デフォルトの Source
が内部的にミューテックスで保護されていること)を明示的にドキュメント化することで、開発者の誤解を防ぎ、並行プログラミングにおける安全な使用を促進するための重要な改善です。