>>返回《C# 併發編程》html
不管是什麼平臺(ASP.NET 、WinForm 、WPF 等),全部 .NET 程序都包含 同步上下文 概念,而且全部多線程編程人員均可以經過理解和應用它獲益。react
原始多線程web
ISynchronizeInvoke 的誕生數據庫
.NET Framework
首次發佈時,這一通用模式是標準化模式。ISynchronizeInvoke
誕生了。ISynchronizeInvoke 的原理編程
ISynchronizeInvoke
還提供了一個屬性來肯定當前代碼是否已在目標線程上運行。ISynchronizeInvoke
實現,而且開發了一種模式來設計異步組件。ISynchronizeInvoke
不太適合 ASP.NET 異步頁面體系結構。
ISynchronizeInvoke
模式開發的異步組件在 ASP.NET 頁面內沒法正常工做,由於 ASP.NET 異步頁面不與單個線程關聯。SynchronizationContext
取代了 ISynchronizeInvoke
。ISynchronizeInvoke
知足了兩點需求:安全
設計 SynchronizationContext
是爲了替代 ISynchronizeInvoke
,但完成設計後,它就不只僅是一個替代品了。服務器
SynchronizationContext
提供了一種方式,可使工做單元列隊並列入上下文。
SynchronizationContext
實現都不是基於單個特定線程的。SynchronizationContext
不包含用來肯定是否必須同步的機制,由於這是不可能的。
Dispatcher.Invoke
是將委託列入上下文,不等委託執行直接返回txtUName.Invoke
會啓動一個process,等到委託執行完畢後返回void OperationCompleted()
。// SynchronizationContext API的重要方面 class SynchronizationContext { // 將工做分配到上下文中 void Post(..); // (asynchronously 異步) void Send(..); // (synchronously 同步) // 跟蹤異步操做的數量。 void OperationStarted(); void OperationCompleted(); // 每一個線程都有一個Current Context。 // 若是「Current」爲null,則按照慣例, // 最開始的當前上下文爲 new SynchronizationContext()。 static SynchronizationContext Current { get; } //設置當前同步上下文 static void SetSynchronizationContext(SynchronizationContext); }
不一樣的框架和主機能夠自行定義上下文多線程
經過了解這些不一樣的實現及其限制,能夠清楚瞭解 SynchronizationContext
概念能夠和不能夠實現的功能併發
位於:System.Windows.Forms.dll:System.Windows.Forms
框架
WinForm
WindowsFormsSynchronizationContext
UI Control
的每一個線程的當前上下文SynchronizationContext
使用 UI Control
的 Invoke
等方法(ISynchronizeInvoke
派生出來的),該方法將委託傳遞給基礎 Win32
消息循環WindowsFormsSynchronizationContext
的上下文是一個單例的 UI 線程WindowsFormsSynchronizationContext
列隊的全部委託一次一個地執行
位於:WindowsBase.dll:System.Windows.Threading
WPF
Dispatcher
中列隊Dispatcher.Run
開啓 循環調度器 時,將這個初始化完成的 同步上下文 安裝到當前上下文DispatcherSynchronizationContext
的上下文是一個單獨的 UI 線程。DispatcherSynchronizationContext
的全部委託均由特定的UI線程一次一個按其排隊的順序執行DispatcherSynchronizationContext
,即便它們都使用相同的基礎調度程序也是如此。調度線程池線程的同步上下文。
位於:mscorlib.dll:System.Threading
Default SynchronizationContext
是默認構造的 SynchronizationContext
對象。
Default SynchronizationContext
。Default SynchronizationContext
將其異步委託列隊到 ThreadPool
,但在調用線程上直接執行其同步委託。Default SynchronizationContext
涵蓋全部 ThreadPool
線程以及任何調用 Send
的線程。Send
的線程們,將這些線程放入這個上下文,直至委託執行完成
Default SynchronizationContext
應用於 線程池 線程,除非代碼由 ASP.NET 承載。Default SynchronizationContext
還隱式應用於顯式子線程(Thread 類的實例),除非子線程設置本身的 SynchronizationContext
。所以,UI 應用程序一般有兩個同步上下文:
UI SynchronizationContext
Default SynchronizationContext
BackgroundWorker
運行流程
BackgroundWorker
捕獲並使用調用 RunWorkerAsync
的線程的 同步上下文Default SynchronizationContext
中執行DoWork
RunWorkerCompleted
事件UI同步上下文 中只有一個 BackgroundWorker
,所以 RunWorkerCompleted
在 RunWorkerAsync
捕獲的 UI同步上下文中執行(以下圖)。
UI同步上下文中的嵌套 BackgroundWorker
BackgroundWorker
從其 DoWork
處理程序啓動另外一個 BackgroundWorker
BackgroundWorker
不會捕獲 UI同步上下文DoWork
由 線程池 線程使用 默認同步上下文 執行。
RunWorkerAsync
將捕獲默認 SynchronizationContext
RunWorkerCompleted
默認狀況下,控制檯應用程序 和 Windows服務 中的全部線程都只有 Default SynchronizationContext
,這會致使一些基於事件的異步組件失敗(也就是沒有UI同步上下文的特性)
Nito.Async
庫的 ActionThread
類可用做通用同步上下文實現。位於:System.Web.dll:System.Web [internal class]
ASP.NET
SynchronizationContext
在線程池線程執行頁面代碼時安裝完成。AspNetSynchronizationContext
中時,它設置原始頁面的 identity 和 culture 到此線程,而後直接執行委託
Post
「異步」列入的,也會直接調用委託。從概念上講, AspNetSynchronizationContext
的上下文很是複雜。
AspNetSynchronizationContext
確保一次只執行其中一項。它們能夠在任意線程上執行,但該線程將具備原始頁面的 identity 和 culture。一個常見的示例:
在異步網頁中使用 WebClient.DownloadDataAsync 將捕獲當前 SynchronizationContext
,以後在該上下文中執行其 DownloadDataCompleted
事件。
DownloadDataAsync
,而後返回;
WebClient
對象下載所請求的數據後,它將在線程池線程上收到通知
DownloadDataCompleted
SynchronizationContext
提供了一種途徑,能夠在不少不一樣框架中編寫組件
BackgroundWorker
和 WebClient
就是兩個在 WinForm
、WPF
、Console
和 ASP.NET Application
中一樣應用自如的組件。在設計這類可重用組件時,必須注意幾點:
ISynchronizeInvoke.InvokeRequired
的等效項
Concrol
對象進行方法調用時,調用方是否必須經過 Invoke
進行調用(傳入委託)。Control
)對象被綁定到特定線程,而且不是線程安全的。Invoke
方法將對相應線程調用的委託列隊WindowsFormsSynchronizationContext
確實 1:1 映射到一個線程(只要不調用 SynchronizationContext.CreateCopy
)
SynchronizationContext.Post
方法不必定是異步的
AspNetSynchronizationContext
是一個明顯的例外同步上下文實現類的摘要
使用特定線程 執行委託 | 獨佔 (一次執行一個委託) | 有序 (委託按隊列順序執行) | Send 能夠直接調用委託 | Post 能夠直接調用委託 | ||
---|---|---|---|---|---|---|
Winform | 能 | 能 | 能 | 若是從UI線程調用 | 從不 | |
WPF/Silverlight | 能 | 能 | 能 | 若是從UI線程調用 | 從不 | |
Default | 不能 | 不能 | 不能 | Always | 從不 | |
ASP.NET | 不能 | 能 | 不能 | Always | Always |
AsyncOperationManager
和 AsyncOperation
類是 SynchronizationContext
抽象類的輕型包裝
AsyncOperation
的異步是使用抽象的同步上下文進行封裝的AsyncOperationManager
在第一次建立 AsyncOperation
時捕獲當前同步上下文 ,若是當前同步上下文爲null
,則使用 Default
同步上下文AsyncOperation
將委託異步發佈到捕獲的 同步上下文AsyncOperationManager
和 AsyncOperation
新組件不該使用基於事件的異步模式
Task
的 API 是 .NET 中異步編程的發展方向BackgroundWorker
和 WebClient
這樣的簡單組件是隱式自帶的
SynchronizationContext
公開 API,Libraries 不只得到了框架獨立性,並且爲高級最終用戶提供了一個可擴展點。ExecutionContext
WCF 有兩個用於配置服務器和客戶端行爲的特性:
ServiceBehaviorAttribute
和 CallbackBehaviorAttribute
UseSynchronizationContext
設置爲 false
能夠禁止 WCF 自動使用 同步上下文
WorkflowInstance
類及其派生的 WorkflowApplication
類的SynchronizationContext
屬性
若是承載進程建立本身擁有的 WorkflowInstance
,同步上下文也許直接設置了
WorkflowInvoker.InvokeAsync
也使用 同步上下文
internal
的 WorkflowApplication
Post
工做流完成事件以及工做流活動TaskScheduler.FromCurrentSynchronizationContext
TPL 使用 Task
對象做爲其工做單元並經過 TaskScheduler
執行。
TaskScheduler
的做用相似於 Defalut 同步上下文 ,將 Task
在 ThreadPool
中列隊。TaskScheduler
,將 Task
在 一個同步上下文 中列隊
Task
中完成,以下所示。UI 進度條更新
private void button1_Click(object sender, EventArgs e) { // 捕獲當前 SynchronizationContext 的 TaskScheduler. TaskScheduler taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); // Start a new task (this uses the default TaskScheduler, // so it will run on a ThreadPool thread). Task.Factory.StartNew(() => { // We are running on a ThreadPool thread here. // Do some work. // Report progress to the UI. Task reportProgressTask = Task.Factory.StartNew(() => { // We are running on the UI thread here. // Update the UI with our progress. },CancellationToken.None, TaskCreationOptions.None, taskScheduler); reportProgressTask.Wait(); // Do more work. }); }
CancellationToken.Register
CancellationToken
類可用於任意類型的取消操做CancellationToken
將該委託列入 同步上下文 隊列,而後纔會進行執行ObserveOn
、 SubscribeOn
和 SynchronizationContextScheduler
Rx 是一個庫,它將事件視爲數據流
ObserveOn(context)
運算符經過一個 同步上下文 將事件列隊SubscribeOn(context)
運算符經過一個 同步上下文 將對這些事件的訂閱 列隊ObserveOn(context)
一般用於使用傳入事件更新 UI,SubscribeOn 用於從 UI 對象使用事件Rx 還有它本身的工做單元列隊方法: IScheduler
接口。
SynchronizationContextScheduler
SynchronizationContextScheduler(SynchronizationContext context)
await
、 ConfigureAwait
、 SwitchTo
和 Progress<T>
await
關鍵字處被捕獲await
關鍵字後時恢復
await
關鍵字後面的執行代碼會被列入到 該同步上下文 中執行
null
時,才捕獲當前 同步上下文null
,則捕獲當前 TaskScheduler
private async void button1_Click(object sender, EventArgs e) { // 當前 SynchronizationContext 被 await 在暗中捕獲 var data = await webClient.DownloadStringTaskAsync(uri); // 此時,已捕獲的SynchronizationContext用於恢復執行, // 所以咱們能夠自由更新UI對象。 }
ConfigureAwait
提供了一種途徑避免 SynchronizationContext
捕獲;
continueOnCapturedContext
參數傳遞 false
會阻止 await
後的代碼,在 await
執行前的 同步上下文 上執行SwitchTo
async
的方法 能夠經過調用 SwitchTo
改變到一個不一樣的同步上下文上,並 awaiting 結果報告異步操做進展的通用模式:
IProgress<T>
接口及其實現 Progress<T>
ProgressChanged
事件返回 void
的 async
方法
這一行爲使返回 void
的 async
方法 相似於頂級異步操做。