深入理解 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.Invoke 或 Dispatcher.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 |
