別のスレッドによって変更された int が特定の値になるまでスピンするスレッドがあります。
int cur = this.m_cur;
while (cur > this.Max)
{
// spin until cur is <= max
cur = this.m_cur;
}
これが機能するには、this.m_cur を volatile と宣言する必要がありますか? コンパイラの最適化により、これが永遠にスピンする可能性はありますか?
別のスレッドによって変更された int が特定の値になるまでスピンするスレッドがあります。
int cur = this.m_cur;
while (cur > this.Max)
{
// spin until cur is <= max
cur = this.m_cur;
}
これが機能するには、this.m_cur を volatile と宣言する必要がありますか? コンパイラの最適化により、これが永遠にスピンする可能性はありますか?
はい、それは難しい要件です。ジャストインタイム コンパイラは、m_cur の値をメモリから更新せずにプロセッサ レジスタに格納できます。実際、x86 のジッターは発生しますが、x64 のジッターは発生しません (少なくとも最後に確認したときは)。
この最適化を抑制するには、 volatileキーワードが必要です。
揮発性は、メモリ モデルが弱いプロセッサである Itanium コアではまったく異なる意味を持ちます。残念ながら、それが MSDN ライブラリと C# 言語仕様に組み込まれた理由です。ARM コアでそれが何を意味するのかはまだわかりません。
以下のブログには、C# のメモリ モデルに関する魅力的な詳細が記載されています。つまり、volatile キーワードを使用する方が安全なようです。
http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/
以下ブログより
class Test
{
private bool _loop = true;
public static void Main()
{
Test test1 = new Test();
// Set _loop to false on another thread
new Thread(() => { test1._loop = false;}).Start();
// Poll the _loop field until it is set to false
while (test1._loop == true) ;
// The loop above will never terminate!
}
}
while ループを終了させる方法は 2 つあります。 _loop フィールドへのすべてのアクセス (読み取りと書き込み) を保護するためにロックを使用する _loop フィールドを揮発性としてマークする 不揮発性フィールドの読み取りが観察される理由は 2 つあります。古い値: コンパイラの最適化とプロセッサの最適化。
m_cur がどのように変更されているかによって異なります。などの通常の割り当てステートメントを使用している場合m_cur--;
は、揮発性である必要があります。ただし、Interlocked操作のいずれかを使用して変更されている場合は、Interlocked のメソッドがメモリ バリアを自動的に挿入して、すべてのスレッドがメモを取得できるようにするため、変更されません。
一般に、Interlocked を使用して、スレッド間で共有されるアトミック値を変更することが望ましいオプションです。メモリバリアを処理するだけでなく、他の同期オプションよりも少し高速になる傾向があります.
とはいえ、他の人が言っているように、ポーリング ループは非常に無駄です。待機が必要なスレッドを一時停止し、m_cur を変更している人は誰でも、その時が来たらそのスレッドを起動する責任を負わせたほうがよいでしょう。特定のニーズに応じて、 Monitor.Wait() と Monitor.Pulse()とAutoResetEventの両方がタスクに適している場合があります。