Dot-Net

為什麼從不同執行緒更新 UI 的模式沒有內置到 .NET 框架中?

  • August 27, 2021

我知道“為什麼我的這個框架像/不像 xyz?” 問題有點危險,但我想看看我錯過了什麼。

在 WinForms 中,您不能從另一個執行緒更新 UI。大多數人使用這種模式

private void EventHandler(object sender, DirtyEventArgs e)
{
   if (myControl.InvokeRequired)
       myControl.Invoke(new MethodInvoker(MethodToUpdateUI), e);
   else
       MethodToUpdateUI(e);
}

private void MethodToUpdateUI(object obj) 
{
   // Update UI
}

更聰明的是這種模式

public static TResult SafeInvoke(this T isi, Func call) where T : ISynchronizeInvoke
{
   if (isi.InvokeRequired) { 
       IAsyncResult result = isi.BeginInvoke(call, new object[] { isi }); 
       object endResult = isi.EndInvoke(result); return (TResult)endResult; 
   }
   else
       return call(isi);
}

public static void SafeInvoke(this T isi, Action call) where T : ISynchronizeInvoke
{
   if (isi.InvokeRequired)
       isi.BeginInvoke(call, new object[] { isi });
   else
       call(isi);
}

不管使用哪種,每個人都必須編寫樣板程式碼來處理這個令人難以置信的常見問題。那麼,為什麼沒有更新 .NET Framework 來為我們執行此操作?是不是程式碼庫的這個區域被凍結了?是否擔心它會破壞向後兼容性?當某些程式碼在版本 N 中以一種方式工作而在版本 N+1 中以不同方式工作時,是否會引起混淆?

我認為首先提到為什麼會有一個 UI 執行緒可能會很有趣。這是為了降低 UI 組件的生產成本,同時提高它們的正確性和健壯性。

執行緒安全的基本問題是,如果在讀取發生時寫入執行緒已完成一半,則可以觀察到私有狀態的非原子更新在讀取執行緒上已完成一半。

為了實現執行緒安全,您可以做很多事情。

  1. 顯式鎖定所有讀寫。優點:最大的靈活性;一切都適用於任何執行緒。缺點:最大的痛苦;一切都必須一直鎖定。可以爭用鎖,這使它們變慢。寫死鎖很容易。編寫無法很好地處理重入的程式碼是很容易的。等等。

  2. 只允許在創建對象的執行緒上進行讀寫。您可以在多個執行緒上擁有多個對象,但是一旦線上程上使用了一個對象,那就是唯一可以使用它的執行緒。因此不會同時在不同的執行緒上讀寫,所以你不需要鎖定任何東西。這是“公寓”模型,也是絕大多數 UI 組件建構時所期望的模型。唯一需要鎖定的狀態是不同執行緒上的多個實例共享的狀態,這很容易做到。

  3. 只允許在擁有的執行緒上進行讀寫,但當沒有正在進行的讀寫時,允許一個執行緒顯式地將所有權移交給另一個執行緒。這是“租用”模型,它是 Active Server Pages 用來回收腳本引擎的模型。

由於絕大多數 UI 組件都是為在公寓模型中工作而編寫的,並且使所有這些組件都成為自由執行緒是痛苦和困難的,因此您不得不在 UI 執行緒上完成所有 UI 工作。

引用自:https://stackoverflow.com/questions/4004918