2014-02-05 63 views
1

今天我們在課堂上學習多線程,並且遇到了一個非常奇怪的錯誤。在我們的新線程中執行for循環時,for循環的上界不斷獲得通過。該線程正在被殺死,但接着會出現另一個值並結束另一個線程。For循環多線程傳遞上限

爲了調試錯誤,我將上限改爲90以避免進度條上出現OutOfRange異常。

將計數器輸出到進度條並更新進度條時,我在輸出窗口中獲得了該值。

while updating progress bar

如果我註釋掉的更新進度條(pbLoad.Value = i;)對我在輸出窗口得到這個

while not updating the progress bar

我曾試圖改變環路i<101,也試圖移動i++是但沒有區別

編輯:這是來自BeginInvoke。當我切換到Invoke它工作,但然後嘗試使用取消按鈕時,我會得到一個死鎖。

下面是代碼:

public partial class Form1 : Form 
    { 
     Thread backgroundThread; 
     bool stopExecution = false; 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void btnStart_Click(object sender, EventArgs e) 
     { 
      stopExecution = false; 
      btnStart.Enabled = false; 
      backgroundThread = new Thread(DoDomethingThatTakesAWhile); 
      backgroundThread.Start(); 
     } 

     private void DoDomethingThatTakesAWhile() 
     { 
      for (int i = 0; i <= 100; i++) 
      { 
       if (!stopExecution) 
       { 
        Thread.Sleep(100); 

        if (pbLoad.InvokeRequired) 
        { 
         MethodInvoker myMethod 
          = new MethodInvoker(
           delegate 
           { 
            if (!stopExecution) 
            { 
             pbLoad.Value = i; 
             Debug.WriteLine(i); //i to output window           
            } 
           }); 
         pbLoad.BeginInvoke(myMethod); 
        } 
        else 
        { 
         pbLoad.Value = i; 
        } 
       } 
       else 
       { 
        break; 
       } 
      } 
     } 

     private void btnCancel_Click(object sender, EventArgs e) 
     { 
      //backgroundThread.Abort(); 
      stopExecution = true; 
      backgroundThread.Join(); 

      pbLoad.Value = 0; 
      btnStart.Enabled = true; 
     } 
    } 
+1

聽起來像是競態條件,在循環完全結束處理之前線程正在遞增值。 – Pseudonym

+0

該值只在該線程中被修改和訪問。 –

回答

1

當您撥打MethodInvoke它不會發生在那一刻,但一段時間後

在你的情況下,您有以下到的機會出現:

  • 調用最終執行的代碼;
  • 循環已經結束(和i成爲101)
  • 你直接訪問i,你看101

並修復它,你可以做的i(複印件通過將其作爲參數調用的方法):

pbLoad.BeginInvoke(new Action<int>(a => 
{ 
    if (!stopExecution) 
    { 
     pbLoad.Value = a; 
     Debug.WriteLine(a); //a to output window           
    } 
}), new object[] { i }); 

PS:你並不需要檢查InvokeRequired,除非你打算直接調用DoDomethingThatTakesAWhile方法,我認爲不是這樣的。

1

您使用BeginInvoke其中明確打開了比賽的可能性。我建議同步調用。

此外,您正在捕獲i,而不是其價值。這是活潑的,只是因爲你在睡覺而意外工作。

任何一項更改都可以解決問題。做他們兩個。

如果可以,請取消此低級別同步使用並使用async/await。

+0

我很確定'任務'是今天走的路(不是異步/等待)。但是我很傷心大家在'winforms'主題中說'wpf',在'thread'主題中說'taks'。我哭了。 – Sinatr

+0

@Sinatr'await'是任務完成後用於恢復的機制。這兩個是互補的技術。您仍然可以使用仍然有用的線程備份任務。 – usr