原文地址:http://msdn.microsoft.com/zh-cn/magazine/gg598924.aspx
很遗憾,很多开发人员甚至不知道这个有用的工具。
无论是什么平台(ASP.NET、Windows 窗体、Windows Presentation Foundation (WPF)、Silverlight 或其他),所有 .NET 程序都包含 SynchronizationContext 概念,并且所有多线程编程人员都可以通过理解和应用它获益。
SynchronizationContext 的必要性
每个要以这种方式使用 Windows 消息队列的多线程程序都必须定义自己的自定义 Windows 消息以及处理约定。
ISynchronizeInvoke 诞生了。
Windows 窗体提供了唯一的 ISynchronizeInvoke 实现,并且开发了一种模式来设计异步组件,这样皆大欢喜。
使用异步页面,处理请求的线程可以开始每个操作,然后返回到 ASP.NET 线程池;当操作结束时,ASP.NET 线程池的另一个线程可以完成该请求。
经过精心设计,SynchronizationContext 取代了 ISynchronizeInvoke。
SynchronizationContext 的概念
设计 SynchronizationContext 是为了替代 ISynchronizeInvoke,但完成设计后,它就不仅仅是一个替代品了。
SynchronizationContext 不包含用来确定是否必须同步的机制,因为这是不可能的。
线程可以更改其当前上下文,但这样的情况非常少见。
大多数情况下,捕获到当前 SynchronizationContext 时,计数递增;捕获到的 SynchronizationContext 用于将完成通知列队到上下文中时,计数递减。
中列出了一些最为重要的方面。
SynchronizationContext API 的各方面
- // The important aspects of the SynchronizationContext APIclass SynchronizationContext
- {
- // Dispatch 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);
- }
SynchronizationContext 的实现
我将简单讨论部分实现。
WindowsFormsSynchronizationContext 的上下文是一个单独的 UI 线程。
当前实现为每个 UI 线程创建一个 WindowsFormsSynchronizationContext。
DispatcherSynchronizationContext 的上下文是一个单独的 UI 线程。
当前实现为每个顶层窗口创建一个 DispatcherSynchronizationContext,即使它们都使用相同的基础调度程序也是如此。
根据惯例,如果一个线程的当前 SynchronizationContext 为 null,那么它隐式具有一个默认 SynchronizationContext。
线程。
因此,UI 应用程序通常有两个同步上下文:包含 UI 线程的 UI SynchronizationContext 和包含 ThreadPool 线程的默认 SynchronizationContext。
图 2)。
UI 上下文中只有一个 BackgroundWorker
图 3)。
UI 上下文中的嵌套 BackgroundWorker
nitoasync.codeplex.com) 可用作通用 SynchronizationContext 实现。
即使委托是通过调用 Post“异步”列队的,也会直接调用委托。
这些可能是启动请求的线程,但更可能是操作完成时处于空闲状态的任何线程。
它们可以在任意线程上执行,但该线程将具有原始页面的标识和区域。
该上下文将保持在相同的线程中,但会确保事件处理程序使用正确的标识和区域运行。
有关 SynchronizationContext 实现的注意事项
但是,在设计这类可重用组件时,必须注意几点。
不过,这不是多大的缺点;代码更为清晰,并且更容易验证它是否始终在已知上下文中执行,而不是试图处理多个上下文。
默认 SynchronizationContext 不保证执行顺序或同步顺序。
一般而言,最好不要假设任何上下文实例将在任何指定线程上运行。
总结了这些不同的实现。
SynchronizationContext 实现摘要
| 使用特定线程执行委托 | 独占(一次执行一个委托) | 有序(委托按队列顺序执行) | Send 可以直接调用委托 | Post 可以直接调用委托 | |
| Windows 窗体 | 能 | 能 | 能 | 如果从 UI 线程调用 | 從不 |
| WPF/Silverlight | 能 | 能 | 能 | 如果从 UI 线程调用 | 從不 |
| 默认 | 不能 | 不能 | 不能 | Always | 從不 |
| ASP.NET | 不能 | 能 | 不能 | Always | Always |
AsyncOperationManager 和 AsyncOperation
AsyncOperation 将委托异步发布到捕获的 SynchronizationContext。
对于这些类型的操作,可以直接捕获和使用 SynchronizationContext。
基于任务的 API 是 .NET 中异步编程的发展方向。
SynchronizationContext 的库支持示例
通过使用 SynchronizationContext 公开 API,库不仅获得了框架独立性,而且为高级最终用户提供了一个可扩展点。
当恢复 ExecutionContext 时,通常也会恢复 SynchronizationContext。
此特性的默认值为 true,这表示在创建通信通道时捕获当前 SynchronizationContext,这一捕获的 SynchronizationContext 用于使约定方法列队。
在这类情况下,将 UseSynchronizationContext 设置为 false 可以禁止 WCF 自动使用 SynchronizationContext。
msdn.microsoft.com/magazine/cc163321)。
部分 .NET Framework 4 升级在 WorkflowInstance 类及其派生类 derived WorkflowApplication 上包含 SynchronizationContext 属性。
然后该 SynchronizationContext 用于发布工作流完成事件以及工作流活动。
所示。
UI 更新的进度报告
- private void button1_Click(object sender, EventArgs e)
- {
- // This TaskScheduler captures SynchronizationContext.Current.
- 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 将该委托列入 SynchronizationContext 队列而不是直接执行它。
ObserveOn 通常用于使用传入事件更新 UI,SubscribeOn 用于从 UI 对象使用事件。
Rx 包含 SynchronizationContextScheduler,这是一个列入 SynchronizationContext 的 IScheduler 实现。
仅当它不为 null 时,才捕获当前 SynchronizationContext,如果为 null,则捕获当前 TaskScheduler):
- private async void button1_Click(object sender, EventArgs e)
- {
- // SynchronizationContext.Current is implicitly captured by await.
- var data = await webClient.DownloadStringTaskAsync(uri);
- // At this point, the captured SynchronizationContext was used to resume
- // execution, so we can freely update UI objects.
- }
SynchronizationContext 实例还有一种扩展方法 SwitchTo;使用该方法,任何异步方法都可以通过调用 SwitchTo 并等待结果,更改为不同的 SynchronizationContext。
该类在构造时捕获当前 SynchronizationContext 并在此上下文中引发其 ProgressChanged 事件。
这一行为使返回 void 的异步方法类似于顶层异步操作。
限制和功能
技术精湛的编程人员了解 SynchronizationContext 限制和功能后,可以更好地编写和利用这些类。
nitoprograms.com。