2009-09-02 23 views
3

我的問題是這樣的:是否有可能使用ISynchronizeInvoke.BeginInvoke()重載一個線程?

我有兩個線程,我的UI線程和一個工作線程。我的工作線程運行在一個由窗體實例化的獨立類中,該窗體將其自身作爲ISynchronizeInvoke傳遞給工作類,然後在該接口上使用Invoke來調用它的事件,這些事件向UI提供狀態更新以供顯示。這很奇妙。

我注意到我的後臺線程似乎運行速度很慢,所以我將電話改爲InvokeBeginInvoke,認爲「我只是提供進度更新,它不需要完全同步,沒有傷害完成「,除了現在我越來越奇怪的進度更新。我的進度條更新,但標籤的文本沒有,如果我改變到另一個窗口,並嘗試返回,它的行爲就像UI線程被鎖定,所以我想知道如果我的進度調用(這發生很通常)會重載UI線程以至於它從不處理消息。這是可能的,還是有其他工作在這裏?

回答

3

你明確地重載UI線程。

在第一個樣品,你是(幕後)發送消息到UI線程,等待其進行處理(這是調用,最終依賴於SendMessage的目的),然後發送另一個。與此同時,其他消息可能已入隊(例如WM_PAINT消息)並進行處理。

在您的第二個示例中,通過使用BeginInvoke(最終依賴於PostMessage),您在消息隊列中大量排入消息,消息泵必須按順序處理。當然,雖然它處理了成千上萬條消息,但它無法處理讓您的用戶界面看起來「凍結」的操作系統消息(WM_PAINT等)。

您可能提供的狀態更新太多;儘量降低反饋水平。

如果您想更好地瞭解消息在Windows中的工作方式,請從this開始。

1

有一些想法;

  • 嘗試批量更新;例如,在循環中每迭代沒有更新;取決於速度,可能每50/500。在列表的情況下,你可以在本地列表變量中緩衝,通過Invoke/BeginInvoke取得列表,並且在UI線程上處理緩衝區
  • 變量捕獲;如果您使用的是BeginInvoke和匿名方法,您可能會遇到問題...我將在
  • 之下添加一個示例
  • 使UI更新有效 - 尤其是如果您正在處理列表;一些控件(特別是基於列表的控件)有一對像BeginEdit/EndEdit這樣的方法,當您進行大量更新時停止UI重繪;相反,它等待,直到End*

捕獲問題...想象(工人):

List<string> stuff = new List<string>(); 
for(int i = 0 ; i < 50000 ; i++) { 
    stuff.Add(i.ToString()); 
    if((i % 100) == 0) { 
     // update UI 
     BeginInvoke((MethodInvoker) delegate { 
      foreach(string s in stuff) { 
       listBox.Items.Add(s); 
      } 
     }); 
    } 
} 

你有沒有注意到,在某些時候兩個線程都說話stuff?UI線程可以迭代它,而工作線程(一直運行在BeginInvoke之後)繼續添加。這可能會導致問題。通常不會性能問題(除非您捕捉異常並花費很長時間來記錄它們),但肯定會有問題。此選項將包括:

  • 使用Invoke運行更新同步
  • 創建每次更新一個新的緩衝,使兩個線程永遠不會具有相同的列表實例(你需要在非常仔細地看雖然)
相關問題