導入
Go メモリモデルは、あるゴルーチンが変数への読み取りを行った際に、異なるゴルーチンが同じ変数への書き込みで生成した値を観測できることが保証される条件を規定しています。
アドバイス
複数のゴルーチンが同時にアクセスするデータを変更するプログラムは、そのようなアクセスを直列化しなければなりません。
アクセスを直列化するには、チャネル操作や、sync
パッケージやsync/atomic
パッケージにある他の同期プリミティブでデータを保護してください。
プログラムの動作を理解するためにこのドキュメントの残りの部分を読む必要があるなら、あなたは巧妙すぎます。
巧妙になってはいけません。
非公式な概要
Goは、言語の他の部分と同様に、セマンティクスをシンプルで理解しやすく、有用なものに保つことを目的として、メモリモデルにアプローチしています。この節では、そのアプローチの一般的な概要を説明しており、ほとんどのプログラマーにとって十分なものです。メモリモデルは、次の節でより正式に規定されています。
データレースは、あるメモリ位置への書き込みが、同じ位置への別の読み取りまたは書き込みと同時に発生することとして定義されます。ただし、関与するすべてのアクセスが sync/atomic
パッケージによって提供されるアトミックデータアクセスである場合は除きます。すでに述べたように、プログラマーはデータレースを回避するために適切な同期を使用することが強く推奨されます。データレースがない場合、Goプログラムはすべてのゴルーチンが単一のプロセッサー上で多重化されているかのように動作します。この特性は、DRF-SC(データレースフリープログラムは逐次一貫性で実行される)と呼ばれることがあります。
プログラマーは、データレースのないGoプログラムを書くべきですが、Go実装がデータレースに対応して行えることには制限があります。実装は、データレースを検出すると、常にレースを報告してプログラムを終了することができます。それ以外の場合、単一ワードサイズまたはサブワードサイズのメモリ位置の各読み取りは、そのメモリ位置に実際に書き込まれた値(おそらく並行して実行されているゴルーチンによって)を観測し、まだ上書きされていない値を観測しなければなりません。これらの実装制約により、GoはJavaやJavaScriptにより近くなります。これらの言語では、ほとんどのレースは限られた数の結果しか持ちません。これは**CやC++**とは異なります。CやC++では、レースを含むプログラムの意味は完全に未定義であり、コンパイラーは何でも行うことができます。Goのアプローチは、間違ったプログラムをより信頼性が高く、デバッグしやすくすることを目的としていますが、それでもレースはエラーであり、ツールがそれらを診断して報告できることを主張しています。