2012-03-08 86 views
0

我一直在研究一個使用BackgroundWorker以定期間隔執行ping操作的工具。我遇到了BackgroundWorker ProgressChanged事件。爲ProgressChanged事件的代碼如下:使用BackgroundWorker和statusstrip更新的CrossThreading問題

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      ProgressUpdated update = (ProgressUpdated)e.UserState; 
      if (sender.ToString() == "System.ComponentModel.BackgroundWorker") 
      { 
       toolStripStatusLabel1.Text = update.GeneralStatus; 
       toolStripProgressBar1.Value = update.ProgressStatus; 
       toolStripStatusLabel2.Text = update.SpecificStatus; 
      } 
      else 
      { 
       toolStripStatusLabel1.Text = update.GeneralStatus; 
       toolStripProgressBar2.Value = update.ProgressStatus; 
       toolStripStatusLabel3.Text = update.SpecificStatus; 
      } 
     } 

的ProgressChanged事件得到雙方在其更新的第一個值BackgroundWork從pingcompletedcallback事件調用時,平結束。當ProgressChanged事件從PingCompletedCallback事件運行時,我只遇到跨線程問題。它在更新第二個進度條時拋出錯誤。

我似乎無法弄清楚爲什麼它發生的其中一個電話,而不是其他。

PingCompletedCallBack是否發生在BackgroundWorker線程上,這就是爲什麼它會導致交叉線程問題?

如果是這樣,我該如何提高事件,以便它將在UI線程上處理,而不是在後臺工作?

編輯:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker worker = sender as BackgroundWorker; 
     // creates ping and sends it async 
     ProgressUpdated args = new ProgressUpdated(string1, int1, string 2); 
     worker.ReportProgress(0,args); 
     // rest of thread for cleanup when cancellation is called 
    } 
private void PingCompletedCallback(object sender, PingCompletedEventArgs e) 
    { 
      // handle the ping response 
      ProgressUpdated update = new ProgressUpdated(string1, int1, string2); 
      ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update); 
      backgroundWorker1_ProgressChanged(this, changed); 
      // handle other types of responses 

    } 

我想利用事件是允許的線程的分離。又名工作者線程引發UI線程正在偵聽的事件,然後在UI線程上處理引發的事件。

由於我的理解錯誤,PingCompletedCallBack是否可以訪問BackgroundWorker的ReportProgress方法?

然後我可以在PingCompletedCallback改變:

ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update); 
backgroundWorker1_ProgressChanged(this, changed); 

到:

backgroundWorker1.ReportProgress(1, update); 

或者我需要改變它在一些其他的方式?

感謝任何人的幫助。

編輯2:

改變ProgrssChanged事件

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      ProgressUpdated update = (ProgressUpdated)e.UserState; 
      toolStripStatusLabel1.Text = update.GeneralStatus; 
      toolStripProgressBar1.Value = update.ProgressStatus; 
      toolStripStatusLabel2.Text = update.SpecificStatus; 
     } 

然後,我創建了第二個更新事件

private void PingUpdate (object sender, ProgressUpdated e) 
    { 
     toolStripStatusLabel1.Text = e.GeneralStatus; 
     toolStripProgressBar2.Value = e.ProgressStatus; 
     toolStripStatusLable3.Text = e.SepcificStatus; 
    } 

我留下的唯一的事情就是打電話從PingCompletedCallback新事件它在UI線程上執行的方式。這是在哪裏使用Invoke語句,還是應該在新事件中使用Invokes?

+0

附註:最好是說 「發件人是BackgroundWorker的」 比sender.ToString()。由於ProgressChanged在UI線程上執行,因此不需要Invoke。 – 2012-03-08 09:09:23

+0

您不應該在backgroundWorker1_ProgressChanged方法中使用任何類型的調用。使用ReportProgress報告進度,因爲這正是這些後臺工作人員的目標;使後臺任務與GUI同步。不要調用backgroundWorker1_ProgressChanged(this,changed),因爲它將從工作線程調用。 – Philippe 2012-03-08 17:26:10

回答

4

BackgroundWorker的文檔聲明您不應該通過DoWork方法操作UI對象,並且應該通過ReportProgress對UI對象進行任何更改。我沒有看過反射器,但它可能會爲您執行隱藏的「調用」。無論是提高PingCompleted事件,可能是在工作線程或其他線程而不是主線程內執行。

您將在Visual Studio調試器的線程窗口中看到DoTask不會在主線程上執行;但是,當調用ReportProgress時,處理程序在主線程上執行。由於您的控件可能是在主線程上創建的,因此您不會看到異常。 enter image description here

現在,如果你試圖在DoWork的方法中顯式調用backgroundWorker1_ProgressChanged,然後backgroundWorker1_ProgressedChanged將會對正在執行DoWork的方法,或者,你的情況相同的線程,這是提高PingCompleted事件的方法上執行: enter image description here

你可以通過你的backgroundWorker1_ProgressChanged處理程序,或路線中加入InvokeRequired檢查可能解決這個跨線程異常的PingCompleted處理程序調用ReportProgress

編輯:

來自PingCompleted處理程序的調用ReportProgress不起作用,因爲您將丟失原始發件人。

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke(new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged), sender, e); 
     return; 
    } 

    // The rest of your code goes here 
} 

編輯2響應:

private void PingUpdate (object sender, ProgressUpdated e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke(new Action<object, ProgressUpdated>(PingUpdate), sender, e); 
     return; 
    } 

    toolStripStatusLabel1.Text = e.GeneralStatus; 
    toolStripProgressBar2.Value = e.ProgressStatus; 
    toolStripStatusLable3.Text = e.SepcificStatus; 
} 
+0

感謝您的視覺效果,我不知道我可以看看這些細節。 PingCompletedCallBack是否可以訪問backgroundWorker以調用ReportProgress? – 2012-03-08 13:49:07

+0

我剛剛注意到您的處理程序有兩個不同的進度條,並且您使用發件人來確定要更新哪個進度欄。如果你使用路由方法,那麼你的'sender'將始終是後臺工作者。你至少有兩個選擇。爲ping完成的事件創建一個單獨的處理程序,並在那裏更新UI組件(使用Invoke)。這將擺脫討厭的(發件人是...)檢查。如果您堅持使用一個處理程序,那麼您將需要在進度處理程序 – Tung 2012-03-08 18:30:40

+0

內添加Invoke檢查,我想我將分開更新調用,以便刪除尷尬的if(sender is ...)檢查。在更新事件中執行Invoke或者在調用事件時更好嗎?見編輯2 – 2012-03-08 19:33:40