2012-05-25 59 views
19

當我在調試模式下運行以下代碼時,它將成功完成並退出。但是,如果我在版本模式下運行以下代碼,它將陷入無限循環並永不完成。釋放模式下的無限循環

static void Main(string[] args) 
{ 
    bool stop = false; 

    new Thread(() => 
    { 
     Thread.Sleep(1000); 
     stop = true; 
     Console.WriteLine("Set \"stop\" to true."); 

    }).Start(); 

    Console.WriteLine("Entering loop."); 

    while (!stop) 
    { 
    } 

    Console.WriteLine("Done."); 
} 

哪種優化會導致它陷入無限循環?

+0

是否不需要在線程之間同步對「stop」的訪問? –

+0

這是由一些optimation的東西造成的。編譯將建立一個真正的停止狀態。 – rekire

+0

看看新的CancellationToken類。他們被髮明來解決這個問題。 http://msdn.microsoft.com/en-us/library/dd997364.aspx –

回答

18

我的猜測是主線程上的stop變量的處理器緩存。在調試模式下,內存模型更加嚴格,因爲調試器需要能夠在所有線程中提供變量狀態的合理視圖。

嘗試進行現場並將其標記爲volatile

volatile bool stop = false; 

static void Main(string[] args) 
{ 

    new Thread(() => 
    { 
     Thread.Sleep(1000); 
     stop = true; 
     Console.WriteLine("Set \"stop\" to true."); 

    }).Start(); 

    Console.WriteLine("Entering loop."); 

    while (!stop) 
    { 
    } 

    Console.WriteLine("Done."); 
} 
+0

易失性有利於正確性,但爲了避免優化,它看起來像是使字段而不是局部變量就足夠了。 –

+0

@AlexeiLevenkov我可能會被誤認爲是錯誤的,但我相信這是CLR的實現細節,理論上(雖然可能不是實際上)可能會發生變化。 –

+0

我想知道爲什麼你不同步訪問變量。我總是這樣做,因爲我認爲這不是一個原子操作來獲取/設置一個'bool'變量。我錯了嗎? –

11

因爲它不是線程安全的,所以更新子線程內的主線程變量stop。它永遠是不可預知的。要處理這種情況,請查看this article

volatile關鍵字指示編譯器生成在每次從該字段讀取的 獲取圍欄,並在 釋放圍欄每寫入該字段。獲取柵欄阻止其他的 讀取/寫入在柵欄之前移動;釋放圍欄 可防止其他讀/寫在圍欄後移動。這些 「半圍牆」比全圍欄更快,因爲它們使 運行時和硬件有更多的優化空間。

0

線程不安全的代碼是不可預測的。主要問題是從另一個線程更改一個線程變量。使變量全局或易變。您可以通過以下

static volatile bool stop = false; 

static void Main(string[] args) 
{  
    new Thread(() => 
    { 
     Thread.Sleep(1000); 
     stop = true; 
     Console.WriteLine("Set \"stop\" to true."); 

    }).Start(); 

    Console.WriteLine("Entering loop."); 

    while (!stop) 
    { 
    } 

    Console.WriteLine("Done."); 
} 
0

做它看起來像某種優化的本地變量的值 - 更改爲一個字段使其終止OK(注意,揮發性或正確鎖定應該在實際代碼中使用):

using System; 
using System.Threading; 

class Program 
{ 
    static bool stop = false; 
    static void Main(string[] args) 
    { 

     new Thread(() => 
     { 
      Thread.Sleep(1000); 
      stop = true; 
      Console.WriteLine("Set \"stop\" to true."); 

     }).Start(); 

     Console.WriteLine("Entering loop."); 

     while (!stop) 
     { 
     } 

     Console.WriteLine("Done."); 
    } 
}