深入理解 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 |