簡體   English   中英

C#編譯器優化和volatile關鍵字

[英]C# compiler optimization and volatile keyword

我已經閱讀了一些關於volatile關鍵字和沒有此關鍵字的行為的帖子。

我特別測試了C#中說明volatile關鍵字的使用答案的代碼。 運行時,我會在發布模式下觀察異常行為,不附加調試器。 到那時為止,沒有問題。

所以,據我所知,以下代碼永遠不會退出。

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) { }
}

在沒有調試器的情況下,在發布模式下,該程序不會退出。

所以,據我所知,以下內容永遠不會退出。

不,它可以停止。 它不能保證

例如,它不會停留在我正在運行的機器上 - 但同樣我可以在另一台機器上嘗試完全相同的可執行文件,它可能表現良好。 它將取決於它運行的CLR使用的確切內存模型語義。 這將受到底層架構的影響,甚至可能使用正確的CPU。

重要的是要注意,不是C#編譯器決定如何處理volatile字段 - C#編譯器只是使用System.Runtime.CompilerServices.IsVolatile指示元數據的波動性。 然后, JIT可以在遵守相關合同方面找出其意義。

在評論中,您說您的目標是32位x86架構。 這個很重要。 此外,我的答案是假設已經意識到僅僅因為內存模型允許某些事情發生並不意味着它總會發生。

簡短回答:

這是因為while循環是空的。 當然,許多其他微妙的變化也會影響行為。 例如,如果在循環之前調用Console.WriteLineThread.MemoryBarrier ,則行為將發生變化。

答案很長:

32位和64位運行時的行為方式有所不同。 無論出於何種原因,32位運行時都是while循環之前沒有顯式/隱式內存生成器或者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進行了測試。 我相信你也應該注意到環境的變化。 嘗試上面的修改。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM