CLR4.0スレッドで問題解析を終了できません

4051 ワード

今日はsocialを見ています.msdn.microsoft.comフォーラムの上で1つの招待状がこの問題を提出しました:
The following peice of code is from C# 4.0 in a Nutshell. Ideally/logically this program should finish within 2-3 seconds, but it seems be caught in an infinite loop. Can someone explain why this happens and how one knows in advance that such a thing will happen for someother code that he/she has written. And who do you think is at fault here OS or .NET?( 元の接続 )
問題のあるコードは次のとおりです.
static void Main()

{

    bool complete = false;

    var t = new Thread(() =>

    {

        //Console.WriteLine("in thread");

        bool toggle = false;

        while (!complete)

        {

            //Console.WriteLine("in loop");

            //Console.WriteLine(complete);

            toggle = !toggle;

        }

    });

    Console.WriteLine("starting thread");

    t.Start();

    Console.WriteLine("thread start");

    Thread.Sleep(1000);

    Console.WriteLine("setting complete");

    complete = true;

    Console.WriteLine("complete set");

    Console.WriteLine("Complete"+ complete);

    t.Join(); // Blocks indefinitely

    Console.WriteLine("done");

}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
ここで、コードは4.0で実行され、コードの表面の意味によって、1秒後にcompleteの値がtrueに変更され、ループの条件が満たされず、スレッドが終了しますが、実際の結果はスレッドが終了できません.なぜですか.
このコードの表面的な意味は間違いないが、CLR 4にある.0の最適化の下で(CLR 2.0の最適化はまだこんなに強くありません)、いくつかの隠れた意味が翻訳されて、スレッドの退出を阻止して、正確には、スレッドの中で循環条件がfalseになることを阻止しました.
CLRから見れば,スレッドにはcompleteとtoggleの2つのメモリアドレスがある.このうちtoggleには読み取り操作も書き込み操作もありますが、completeには読み取り操作のみです.
また、CLRに何のHintも与えられていないため、CLRはこのcompleteが他のスレッドによって変更されないと判断するので、completeの値をレジスタで保存するように最適化し、1回目以外はそのままレジスタを読み出す.
これは隠れた隠れた意味であるが,この隠れた意味は行為全体の変化をもたらすのに十分である.どのプログラムも別のスレッドのレジスタの値を変更することはできません.そのため、スレッドの最適化されたcompleteは永遠にfalse、つまり最終的な結果です.スレッドは終了できません.
元スレ主はこれが間違いなのは誰の間違いなのか、OSのか、それとも.Netの、ここで言わざるを得ないのは、このエラーはアプリケーションを書く人とコンパイラを書く人の理解が一致しないことによるものです.
コンパイラを書く人は、マルチスレッドで修正された値であれば、アプリケーションを書く人はコンパイラにHintを与え、Hintのある変数に対してのみ最適化を使用しないと考えます.アプリケーションを書く人は、すべての変数が最適化されないと思って悲劇が発生しました.
悲劇が起きた以上、悲劇の再発を阻止する方法がある以上、このコンパイラに必要なHintはいったい何なのか.--volatileキーワード(またはメモリ同期メカニズム、lockはメモリ同期メカニズムですが、変数を読むためだけにlockを除去することはできません)
volatileの直訳は変わりやすいので、ちょっとわかりにくいですが、例を挙げてみましょう.
ある変数をvolatileの変数として宣言すると、コンパイラ(JIT)はこの変数が変化しやすいことを知っているので、どの読み書き操作もレジスタに最適化できず、メモリアドレスを読み書きしなければならない.
これで、悲劇の発生を阻止することができます.
いつvolatileを使いますか?
簡単に言えば、変数がオンライン外で変更される場合、lockや他のメモリ同期メカニズム(Thread.MemoryBarrierなど)を使用していない場合は、このキーワードが必要です.
このうち、オンライン外で変更された場合は、2つに分けることができます.
  • 変数は、他のスレッドによって
  • に変更されます.
  • 変数はハードウェアによって
  • に変更されます.
    1つ目は理解しやすいし、2つ目はアプリケーションレベルを書く人にとっては理解しにくいし、あまり出会うこともないので、丸暗記して、ほほほ.