isComplete変数に「volatile」キーワードがないため、オプティマイザはだまされていると思います。
もちろん、これはローカル変数なので、追加することはできません。そしてもちろん、これはローカル変数であるため、ローカルはスタックに保持されており、常に「新鮮」であるため、ローカル変数はまったく必要ありません。
ただし、コンパイル後はローカル変数ではなくなります。匿名デリゲートでアクセスされるため、コードは分割され、次のようなヘルパークラスとメンバーフィールドに変換されます。
public static void Main(string[] args)
{
TheHelper hlp = new TheHelper();
var t = new Thread(hlp.Body);
t.Start();
Thread.Sleep(500);
hlp.isComplete = true;
t.Join();
Console.WriteLine("complete!");
}
private class TheHelper
{
public bool isComplete = false;
public void Body()
{
int i = 0;
while (!isComplete) i += 0;
}
}
マルチスレッド環境のJITコンパイラー/オプティマイザーがTheHelperクラスを処理するときに、メソッドの開始時に実際にいくつかのレジスターまたはスタックフレームに値をキャッシュし、メソッドが終了するまで決して更新しないと想像できます。これは、「= true」が実行される前にスレッドとメソッドが終了しないという保証がないためです。保証がない場合は、キャッシュせずに、ヒープオブジェクトを毎回読み取る代わりに1回読み取ることでパフォーマンスを向上させてください。反復。falseBody()
これがまさにキーワードvolatileが存在する理由です。
このヘルパークラスは、であるためには、正しい 小さな少し良く 1)マルチスレッド環境では、それが持っている必要があります。
public volatile bool isComplete = false;
もちろん、自動生成されたコードなので、追加することはできません。より良いアプローチは、へのlock()読み取りと書き込みの周りにs を追加するかisCompleted、またはベアメタル(ベアメタルではない)を実行する代わりに、すぐに使用できる他の同期またはスレッド/タスクユーティリティを使用することです。それは、GC、JIT、および(..)を使用したCLR上のC#であるためです。
デバッグモードの違いは、デバッグモードでは多くの最適化が除外されているために発生する可能性があり、画面に表示されるコードをデバッグできます。したがってwhile (!isComplete)、最適化されていないため、そこにブレークポイントを設定できるためisComplete、メソッドの開始時にレジスターまたはスタックに積極的にキャッシュされず、ループの反復ごとにヒープ上のオブジェクトから読み取られます。
ところで。それは私の推測です。コンパイルするつもりもありませんでした。
ところで。バグではないようです。それは非常にあいまいな副作用のようなものです。また、私がそれについて正しい場合、それは言語の欠陥である可能性があります-C#は、キャプチャーされ、クロージャーのメンバーフィールドに昇格されるローカル変数に「volatile」キーワードを配置できるようにする必要があります。
1)についてエリックリペットからのコメントは以下を参照volatileおよび/または頼っそのコードを確保することに伴う複雑さのレベルを示す、この非常に興味深い記事がvolatileあり、安全 ..uh、良い ..uh、のがOKをしましょう。