2016-12-01 144 views
1

對不起,但我很困惑。我們有一個將命令發送到一個硬件的進程,它會被淹沒。我創建了一個簡單的解決方案,每完成100次發送,它就會暫停1秒,然後繼續處理。以調試模式運行,這完全解決了我們遇到的所有問題。但是,當我將這個解決方案編譯成Release版本時,我的計時器方法似乎永遠停滯不前。發佈版本的運行方式與調試版本不同

在下面的代碼中,我有一個簡單的while循環,循環直到bool爲true。 (我不想使用睡眠,因爲我不想讓線程變得反應遲鈍做)

foreach (DataRow row in ds.Tables[0].Rows) 
{ 
    string Badge = Database.GetString(row, "Badge"); 
    if (Badge.Length > 0) 
    { 
     if(Count < Controller.MaximumBadges) 
     { 
      if (processed == 100) // Every 100 downloads, pause for a second 
      { 
       processed = 0; 
       StartTimer(); 
       while (!isWaitOver) 
       { 
       } 
       Controller.PostRecordsDownloadedOf("Badges", Count); 
      } 

      if (Download(Badge, false)) 
      { 
       Count++; 
       processed++; 
      } 
     } 
     else 
      Discarded++; 
    } 
    TotalCount++; 
} 

private void StartTimer() 
{ 
    // Create a timer with a one second interval. 
    aTimer = new System.Timers.Timer(1000); 
    // Hook up the Elapsed event for the timer. 
    aTimer.Elapsed += OnTimedEvent; 
    aTimer.AutoReset = true; 
    aTimer.Enabled = true; 
    isWaitOver = false; 
} 

private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e) 
{ 
    isWaitOver = true; 
    aTimer.Enabled = false; 
} 

任何人都可以看到一個原因,在發行模式下運行時,while循環會無限地被卡住?此外,如果有人看到更好的解決方案,請讓我知道。但我必須使用VS 2010。

感謝您的閱讀。

+1

make'isWaitOver' volitile。 https://msdn.microsoft.com/en-us/library/x13ttww7.aspx – flakes

+0

您不顯示聲明,但爲了使此代碼可靠,isWaitOver必須是易失性的。是嗎? –

+0

Thread.Sleep是作爲一個循環 – NtFreX

回答

1

您的代碼似乎在其中存在爭用條件。啓動計時器首先啓用計時器,然後將isWaitOver設置爲false。當它運行時,然後將isWaitOver設置爲true。這是不太可能的,但是在繁忙的系統中,定時器可能在主線程設置isWaitOverfalse之前觸發OnTimedEvent。如果發生這種情況,那麼isWaitOver可能總是以虛假形式出現在您的循環中。爲防止這種情況,請在aTimer.Enabled = true之前將您的isWaitOver = false行刪除。

更可能出現的問題是使用優化程序重新排序代碼中的內容。如果單個線程不會注意到這種差異,但它可以在這樣的多線程場景中引發問題,則可以這樣做。要解決此問題,您可以在代碼中製作isWaitOvervolatile或將memory barriers。請參閱Threading in C# by Joseph Albahari以獲得良好的撰寫。

通常,當它達到易失性和內存障礙有所改善的地步時,您已經使自己的代碼變得複雜而脆弱。內存障礙是非常先進的事情,非常容易出錯,幾乎不可能正確測試(例如,行爲因所用的CPU型號而異)。我的建議是將isWaitOver切換爲ManualResetEvent並等待它通過計時器線程發送信號。這還有一個好處,就是可以防止你的代碼進入CPU佔用的自旋循環。

最後你的代碼有一個句柄泄漏。每次你都在創建一個新的Timer對象,但你永遠不會再處理它。你可以在創建一個新的之前處理它,或者只是使用一個而不是重新創建它。

ManualResetEvent isWaitOver = new ManualResetEvent(false); 

    private void Run() 
    { 
     foreach (DataRow row in ds.Tables[0].Rows) 
     { 
      string Badge = Database.GetString(row, "Badge"); 
      if (Badge.Length > 0) 
      { 
       if (Count < Controller.MaximumBadges) 
       { 
        if (processed == 100) // Every 100 downloads, pause for a second 
        { 
         processed = 0; 
         StartTimer(); 
         isWaitOver.WaitOne(); 
         Controller.PostRecordsDownloadedOf("Badges", Count); 
        } 

        if (Download(Badge, false)) 
        { 
         Count++; 
         processed++; 
        } 
       } 
       else 
        Discarded++; 
      } 
      TotalCount++; 
     } 
    } 

    private void StartTimer() 
    { 
     // Create a timer with a one second interval. 
     if (aTimer != null) aTimer.Dispose(); 
     aTimer = new System.Timers.Timer(1000); 
     // Hook up the Elapsed event for the timer. 
     isWaitOver.Reset(); 
     aTimer.Elapsed += OnTimedEvent; 
     aTimer.AutoReset = true; 
     aTimer.Enabled = true; 
    } 

    private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e) 
    { 
     aTimer.Enabled = false; 
     isWaitOver.Set(); 
    } 
+0

你修好了!非常棒!感謝您的詳細解釋。如果我再次需要這樣的解決方案,我會爲未來保留這個解決方案。 –

相關問題