>>返回《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
方法 相似于顶级异步操做。