編集:私はついにこの問題に関する完全な記事を書きました:同期、メモリの可視性、漏れやすい抽象化
このコードを使用して、揮発性読み取りの重要性を示しています。
bool ok = false;
void F()
{
int n = 0;
while (!ok) ++n;
}
public void Run()
{
Thread thread = new Thread(F);
thread.Start();
Console.Write("Press enter to notify thread...");
Console.ReadLine();
ok = true;
Console.WriteLine("Thread notified.");
}
予想どおり、スレッドは新しいok
値を認識せず、プログラムがハングします。
while
しかし、この動作を実現するには、整数をインクリメントするなど、ループ内で何かを行う必要があります。
ステートメントを削除する++n
と、スレッドは新しい値を読み取って終了します。
CILに関する限り(少なくとも私のような素人にとっては)何もないため、 JITterの最適化と関係があると思います。
.method private hidebysig instance void F() cil managed
{
.maxstack 2
.locals init ([0] int32 n)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0008
IL_0004: ldloc.0
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stloc.0
IL_0008: ldarg.0
IL_0009: ldfld bool ThreadingSamples.MemoryVisibilitySample::ok
IL_000e: brfalse.s IL_0004
IL_0010: ret
}
.method private hidebysig instance void F() cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld bool ThreadingSamples.MemoryVisibilitySample::ok
IL_0006: brfalse.s IL_0000
IL_0008: ret
}
逆に、ループ内で何かを実行すると、スレッドがキャッシュの更新をトリガーする可能性が高くなると単純に予想できます。
私は再び何が欠けていますか?
最終編集: これもJITterの黒魔術です。
これが「よく知られた」JITter「問題」であることを確認し、x64では「期待される」動作が得られることを指摘してくれた Hans に感謝します。
結果のアセンブリ コードを提供し、デバッグの知恵を共有してくれたMagnatLUに感謝します。