それが最適化されるかどうかは、コンパイラーとコンパイラーが何を最適化するかによって完全に異なります。C ++ 98/03メモリモデルはx
、その設定と値の取得の間で変更される可能性を認識していません。
C ++ 11メモリモデルは、変更される可能性があることを認識していx
ます。ただし、気にしません。変数への非アトミックアクセス(つまり、std::atomic
sまたは適切なミューテックスを使用しない)は、未定義の動作をもたらします。したがって、C ++ 11コンパイラが、書き込みと読み取りの間で変更がないことを想定することはまったく問題ありません。x
未定義の動作は、「関数がx
変更を確認することはない」ことを意味する可能性があるためです。
それでは、C++11が何を言っているか見てみましょうvolatile int x;
。そこにそれを入れて、他のスレッドが混乱してx
いる場合でも、未定義の動作があります。揮発性はスレッドの動作に影響を与えません。C ++ 11のメモリモデルは、アトミックな読み取りまたは書き込みを定義していませんx
。また、アトミックでない読み取り/書き込みを適切に順序付けるために必要なメモリバリアも必要ありません。volatile
いずれにせよそれとは何の関係もありません。
ああ、あなたのコードはうまくいくかもしれません。しかし、C++11はそれを保証しません。
コンパイラに通知volatile
するのは、その変数からのメモリ読み取りを最適化できないということです。ただし、CPUコアには異なるキャッシュがあり、ほとんどのメモリ書き込みはすぐにメインメモリに出力されません。それらはそのコアのローカルキャッシュに保存され、書き込まれる可能性があります...最終的には。
CPUには、キャッシュラインをメモリに強制的に出力し、異なるコア間でメモリアクセスを同期する方法があります。これらのメモリバリアにより、2つのスレッドが効果的に通信できます。別のコアで書き込まれた1つのコアのメモリから読み取るだけでは十分ではありません。メモリを書き込んだコアはバリアを発行する必要があり、それを読み取っているコアは、実際にデータを取得するために、それを読み取る前にそのバリアを完了している必要があります。
volatile
これのどれも保証しません。Volatileは、「ハードウェア、マップされたメモリなど」で動作します。これは、そのメモリを書き込むハードウェアが、キャッシュの問題を確実に処理するためです。CPUコアが書き込みのたびにメモリバリアを発行した場合、基本的にパフォーマンスの希望に別れを告げることができます。そのため、C ++ 11には、バリアを発行するために構成が必要な場合を示す特定の言語があります。
volatile
メモリアクセス(いつ読み取るか)についてです。スレッド化とは、メモリの整合性(実際にそこに格納されているもの)に関するものです。
C ++ 11メモリモデルは、あるスレッドでの書き込みが別のスレッドで表示されるようにする操作に固有のものです。それはメモリの完全性についてであり、それは何かvolatile
を処理するものではありません。また、メモリの整合性には、通常、両方のスレッドが何かを実行する必要があります。
たとえば、スレッドAがミューテックスをロックし、書き込みを実行してからロックを解除した場合、C ++ 11メモリモデルでは、スレッドBが後でロックした場合にのみ書き込みがスレッドBに表示されるようにする必要があります。実際にその特定のロックを取得するまで、そこにどのような値があるかは未定義です。このようなものは、規格のセクション1.10に詳細に記載されています。
標準に関して、あなたが引用するコードを見てみましょう。セクション1.10、p8は、スレッドを別のスレッドと「同期」させる特定のライブラリ呼び出しの機能について説明しています。他の段落のほとんどは、同期(およびその他のもの)がスレッド間の操作の順序を構築する方法を説明しています。もちろん、あなたのコードはこれを呼び出しません。同期ポイント、依存関係の順序、何もありません。
このような保護がなければ、何らかの形の同期や順序付けがなければ、1.10p21が登場します。
プログラムの実行には、異なるスレッドで2つの競合するアクションが含まれ、そのうちの少なくとも1つがアトミックではなく、どちらももう一方の前に発生しない場合、データ競合が含まれます。このようなデータ競合は、未定義の動作を引き起こします。
プログラムには、2つの競合するアクション(読み取りx
と書き込みx
)が含まれています。どちらもアトミックではなく、同期によって他の前に発生するように順序付けられていません。
したがって、未定義の動作を実現しました。
したがって、C ++ 11メモリモデルによってマルチスレッド動作が保証されるstd::atomic<int> x
唯一のケースは、適切なミューテックスを使用するか、適切なアトミックロード/ストア呼び出しを使用する場合です。
ああ、あなたもx
揮発性にする必要はありません。(非インライン)関数を呼び出すときはいつでも、その関数またはそれが呼び出す何かがグローバル変数を変更する可能性があります。したがって、ループ内の読み取りを最適化することはできません。また、同期するすべてのC ++ 11メカニズムでは、関数を呼び出す必要があります。それはまさにメモリバリアを引き起こすために起こります。x
while