3

volatileこのキーワードを使用しないキーワードと動作に関する投稿をいくつか読みました。

Illustrating usage of the volatile keyword in C#への回答からコードを特にテストしました。実行中、デバッガーが接続されていない状態で、リリース モードで例外の動作を観察します。そこまでは問題ありません。

したがって、私が理解している限り、次のコードは決して終了しないはずです。

public class Program
{
    private bool stopThread;

    public void Test()
    {
        while (!stopThread) { }  // Read stopThread which is not marked as volatile
        Console.WriteLine("Stopped.");
    }


    private static void Main()
    {
        Program program = new Program();

        Thread thread = new Thread(program.Test);
        thread.Start();

        Console.WriteLine("Press a key to stop the thread.");
        Console.ReadKey();

        Console.WriteLine("Waiting for thread.");
        program.stopThread = true;

        thread.Join();  // Waits for the thread to stop.
    }
}

なぜ終了するのですか?デバッガなしでリリースモードでも?

アップデート

C# での volatile キーワードの使用例のコードの適応。

private bool exit;

public void Test()
{
    Thread.Sleep(500);
    exit = true;
    Console.WriteLine("Exit requested.");
}

private static void Main()
{
    Program program = new Program();

    // Starts the thread
    Thread thread = new Thread(program.Test);
    thread.Start();

    Console.WriteLine("Waiting for thread.");
    while (!program.exit) { }
}

このプログラムは、デバッガーが接続されていない場合、リリース モードで終了しません。

4

2 に答える 2

10

したがって、私が理解している限り、以下は決して終了しないでください。

いいえ、停止できます。それは保証されていません。

たとえば、現在実行しているマシンでは停止しませんが、まったく同じ実行可能ファイルを別のマシンで試すこともでき、正常に動作する可能性があります。それが実行される CLR によって使用される正確なメモリ モデルのセマンティクスに依存します。これは、基盤となるアーキテクチャや、使用されている正確な CPU によって影響を受ける可能性があります。

フィールドの処理方法を決定するのはC# コンパイラではないことに注意することが重要ですvolatile。C# コンパイラは、 を使用してメタデータの揮発性を示すだけSystem.Runtime.CompilerServices.IsVolatileです。次に、JITは、関連する契約に従うという意味で、それが何を意味するかを理解することができます。

于 2013-07-28T12:01:11.130 に答える
1

コメントで、32 ビット x86 アーキテクチャをターゲットにしていると言っていました。これは重要。また、私の答えは、メモリモデルが何かを発生させるからといって、それが常に発生するとは限らないことをすでに認識していると仮定します。

簡潔な答え:

whileループが空だからです。もちろん、他の多くの微妙な変更も動作に影響を与える可能性があります。たとえば、ループの前にConsole.WriteLineまたはを呼び出すと、動作が変わります。Thread.MemoryBarrier

長い答え:

32 ビット ランタイムと 64 ビット ランタイムの動作には違いがあります。32 ビット ランタイムは、何らかの理由で、ループの前に明示的/暗黙的なメモリ ジェネレーターがない場合、またはwhileループ自体が空の場合に、リフティング最適化より優先されます。

ここで同じ主題に関する別の質問からの私の例を考えてみましょう。では、また下です。

class Program
{
    static bool stop = false;

    public static void Main(string[] args)
    {
        var t = new Thread(() =>
        {
            Console.WriteLine("thread begin");
            bool toggle = false;
            while (!stop)
            {
                toggle = !toggle;
            }
            Console.WriteLine("thread end");
        });
        t.Start();
        Thread.Sleep(1000);
        stop = true;
        Console.WriteLine("stop = true");
        Console.WriteLine("waiting...");

        // The Join call should return almost immediately.
        // With volatile it DOES.
        // Without volatile it does NOT.
        t.Join(); 
    }
}

この例は、32 ビット x86 ハードウェアでの「終了なし」動作を実際に再現しています。意図的にwhileループをビジー状態にして何かを実行していることに注目してください。何らかの理由で、空のループは動作を一貫して再現しません。ここで、上記から学んだことを使用して最初の例を変更し、何が起こるか見てみましょう。

public class Program
{
    private bool stopThread;

    public void Test()
    {
        bool toggle = true;
        while (!stopThread) // Read stopThread which is not marked as volatile
        { 
          toggle = !toggle;
        }  
        Console.WriteLine("Stopped.");
    }


    private static void Main()
    {
        Program program = new Program();

        Thread thread = new Thread(program.Test);
        thread.Start();

        Console.WriteLine("Press a key to stop the thread.");
        Console.ReadKey();

        Console.WriteLine("Waiting for thread.");
        program.stopThread = true;

        thread.Join();  // Waits for the thread to stop.
    }
}

最初の例のわずかに変更されたバージョンを使用して、whileループに何かを実行させると、「終了なし」の動作を示し始めることがわかります。Windows 7 64 ビットで 32 ビット x86 をターゲットとする .NET 4.5 でこれをテストしました。環境の変化にも気付くべきだと思います。上記の変更で試してみてください。

于 2013-07-28T19:07:01.980 に答える