深入理解 SynchronizationContext

BUTTERAPPLE發表於2021-07-12

深入理解 SynchronizationContext

SynchronizationContext(後續以SC簡稱) 是什麼?

1.1 概念

​ 在 .NET 框架的多執行緒程式中,往往很多時間需要將一個執行緒工作單元或上下文,傳遞給另一個執行緒。我們都知道的是 Windows 上的程式是以 訊息迴圈為中心的,這個如何理解呢?

每一個 window 窗體都有一個與之關聯的 Window Procedure,這個是一個用來處理所有訊息傳送或傳送到類的給所有訊息的函式。窗體的所有UI顯示和顯示都取決於 Window Procedure 對這些訊息的響應。

SynchronizationContext 主要提供了一下三方面的功能:

​ 1) 提供了一種把工作單元 新增到 上下文的佇列中的方法。

​ 2) 每一個執行緒 都有 一個 “current” 的 上下文。

​ 3) 它儲存著未完成非同步運算元的計數,這個數量 隨著 當前的 SC 被捕獲,或被奪取時增加,當捕獲的 SC 用於將完成通知排隊傳送到該上下文時,則減少。

// 重要的 SynchronizationContext APIClass
{
  // Dispather work to the context.
  void Post(); // Asynchronously
  void send(); // Synchronously
  
  // Keep track of the number of asynchronous operations.
  void OperationStarted();
  void OperationCompleted();
  
  // Each thread has a current context.
  // If "Current" is null, then the thread's current context is
  // "new SynchronizationContext()", by convention.
  static SynchronizationContext Current{get;}
  static void SetSynchronizationContext(SynchronizationContext);
}

1.2 SynchronizationContext 的實現

1)WindowsFormsSynchronizationContext (System.Windows.Forms.dll)

​ 1,Use ISynchronizeInvoke on UI Control,用來將委託傳遞 給 win32 message loop

​ 2,每一個 UI 執行緒 都會建立一個 WindowsFormsSynchronizationContext

​ 3,WindowsFormsSynchronizationContext 的上下文是一個 單一的UI 執行緒

2)DispatcherSynchronizationContext(WindowsBase.dll: System.Windows.Threading)

​ 1,以 “Normal” 優先順序的委託 傳遞給 UI 執行緒。

​ 2,所有排隊到 DispatcherSynchronizationContext 的委託都是由 特定的UI執行緒 按照他們排隊的順序 依次執行,一次執行一個。

​ 3,DispatcherSynchronizationContext 的上下文是一個 單一的 UI 執行緒

3)Default(ThreadPool) SynchronizationContext(mscorlib.dll: System.Threading)

​ 1,預設的 SynchronizationContext 是一個預設建構函式的 SynchronizationContext 物件,按照慣例,如果一個執行緒 當前的 SynchronizationContext 是 null,那麼它會 隱式的 含有一個 預設的 SynchronizationContext

​ 2,預設 SynchronizationContext 將它的非同步委託 新增到 執行緒池 佇列,但是 在呼叫的執行緒上執行它的同步委託。因此,它的上下文 涵蓋了 所有的執行緒池的執行緒 以及 呼叫它的執行緒。上下文 “借用” 呼叫它的執行緒,然後把它們帶入到上下文中 直到委託結束。從某種意義上來說,預設上下文可能包含當前程式中的任何執行緒。

​ 3,預設 SynchronizationContext 是應用在 執行緒池中的執行緒的除非是被 ASP.NET 託管的程式碼,預設的 SynchronizationContext 也隱式應用於顯式的子執行緒中除非子執行緒設定了自己的 SynchronizationContext。因此,UI 的應用一般都有兩個 SynchronizationContext, UI 的 SynchronizationContext cover UI thread, default SynchronizationContext cover ThreadPool thread。

圖1 是一個典型比如 WPF 程式,呼叫 Dispatcher.InvokeDispatcher.BeginInvoke 時Context 轉換的一個圖。

private void On_Time_Elapsed(object sender, EventArgs e)
{
		Dispatcher.Invoke(()=>{
      _displayTextBlock.Text = "Show Here.";
    });  
}

圖1

圖2 是 WPF 程式中,Dispatcher.Invoke 中又新開了執行緒池的執行緒執行的例子。

private void On_Time_Elapsed(object sender, EventArgs e){
  Dispatcher.Invoke(()=>{
 			Task.Run(()=>{
        // Do Something here.
      });   
    
    _displayTextBlock.Text = "1111";
  });
}

圖2

各個不同實現的 SynchronizationContext 的區別

Specific Thread Used to Execute Delegates Exclusive (Delegates Execute One at a Time) Ordered (Delegates Execute in Queue Order) Send May Invoke Delegate Directly Post May Invoke Delegate Directly
Windows Forms Yes Yes Yes If called from UI thread Never
WPF/Silverlight Yes Yes Yes If called from UI thread Never
Default No No No Always Never
ASP.NET No Yes No Always Always

參考

1) It's all about SynchronizationContext