2010-07-23 91 views
2

我有一個函數,它將控件添加到父控件,該控件從與創建控件的線程不同的線程調用。這是怎麼一回事呢:即使在UI線程上執行時的跨線程操作

1  delegate void AddControlToParentDelegate(Control child, Control parent); 
2  private void addControlToParent(Control child, Control parent) { 
3  if (parent.InvokeRequired) { 
4   AddControlToParentDelegate d = new AddControlToParentDelegate(addControlToParent); 
5   this.Invoke(d, new object[] { child, parent }); 
6   } else { 
7    parent.Controls.Add(child); 
8   } 
9  } 
10 } 

這工作得很好,直到兩parent.InvokeRequired以及child.InvokeRequired是真實的。然後,一旦執行第5行(現在代理d被調用並且該函數應該在UI線程上運行(對嗎?))child在第7行會引發一個跨線程操作無效異常。爲什麼是這樣?它不是已經在它創建的線程上運行了嗎?

我設法通過增加一個額外(child.InvokeRequired)檢查,以解決這個問題:

delegate void AddControlToParentDelegate(Control child, Control parent); 
private void addControlToParent(Control child, Control parent) { 
    if (parent.InvokeRequired) { 
     AddControlToParentDelegate d = new AddControlToParentDelegate(addControlToParent); 
     this.Invoke(d, new object[] { child, parent }); 
    } else { 
     if (child.InvokeRequired) { 
      this.Invoke(new MethodInvoker(delegate() { 
      parent.Controls.Add(child); 
      })); 
     } else { 
      parent.Controls.Add(child); 
     } 
    } 
} 

但這似乎只是可怕的黑客-Y和不必要的。這是做到這一點的方式嗎?還是我完全錯過了巴士?

回答

6

請注意,InvokeRequired在控件沒有窗口句柄時不可靠。對於新創建的沒有父級控制的子控件來說,這種情況幾乎肯定會發生,這在您的「固定」代碼中似乎就是這種情況。

見伊萬Krivyakov的詳細分析:http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

+3

此外,設計,*不*使用'InvokeRequired'是更易於維護。我建議爲ui線程或使用「BackgroundWorker.ReportProgress」安排一個'Task',它們都是獨立於平臺的解決方案(即不綁定到Windows窗體)。 – 2010-07-23 01:42:21