MSDNweb
那些同时执行多项任务、但仍能响应用户交互的应用程序一般须要实施一种使用多线程的设计方案。System.Threading 命名空间提供了建立高性能多线程应用程序所必需的全部工具,但要想有效地使用这些工具,须要有丰富的使用多线程软件工程的经验。对于相对简单的多线程应用程序,BackgroundWorker 组件提供了一个简单的解决方案。对于更复杂的异步应用程序,请考虑实现一个符合基于事件的异步模式的类。数据库
基于事件的异步模式具备多线程应用程序的优势,同时隐匿了多线程设计中固有的许多复杂问题。使用支持此模式的类,您将可以:编程
“在后台”执行耗时任务(例以下载和数据库操做),但不会中断您的应用程序。windows
同时执行多个操做,每一个操做完成时都会接到通知。网络
等待资源变得可用,但不会中止(“挂起”)您的应用程序。多线程
使用熟悉的事件和委托模型与挂起的异步操做通讯。有关使用事件处理程序和委托的更多信息,请参见事件和委托。异步
支持基于事件的异步模式的类将有一个或多个名为 MethodNameAsync 的方法。这些方法可能会建立同步版本的镜像,这些同步版本会在当前线程上执行相同的操做。此类还可能有一个MethodNameCompleted 事件,并且它可能会有一个 MethodNameAsyncCancel(或只是 CancelAsync)方法。async
PictureBox 是一个支持基于事件的异步模式的典型组件。您能够经过调用其 Load 方法来同步下载图像,可是若是图像很大,或者网络链接很慢,您的应用程序将中止(“挂起”),直到下载操做完成而且对 Load 的调用返回后才会继续执行。工具
若是您但愿您的应用程序在加载图像时保持运行,您能够调用 LoadAsync 方法,处理 LoadCompleted 事件,这与您处理任何其余事件没有什么两样。调用 LoadAsync 方法时,您的应用程序将继续运行,而下载操做将在另外一个线程上(“在后台”)继续。图像加载操做完成时,将会调用您的事件处理程序,您的事件处理程序能够检查 AsyncCompletedEventArgs 参数以肯定下载是否已成功完成。性能
基于事件的异步模式要求异步操做能够取消,PictureBox 控件使用其 CancelAsync 方法来支持此要求。调用 CancelAsync 会提交一个中止挂起的下载的请求,任务取消时会引起 LoadCompleted事件。
![]() |
---|
下载有可能恰在发出 CancelAsync 请求时完成,所以 Cancelled 可能没有反映取消请求。这叫作“争用条件”,是多线程编程中常见的一个问题。有关多线程编程中的问题的更多信息,请参见托管线程处理的最佳作法。 |
基于事件的异步模式能够采用多种形式,具体取决于某个特定类支持的操做的复杂程度。最简单的类可能只有一个 MethodNameAsync 方法和一个对应的 MethodNameCompleted 事件。更复杂的类可能有若干个 MethodNameAsync 方法(每种方法都有一个对应的 MethodNameCompleted 事件),以及这些方法的同步版本。这些类分别支持各类异步方法的取消、进度报告和增量结果。
异步方法可能还支持多个挂起的调用(多个并行调用),容许您的代码在此方法完成其余挂起的操做以前调用此方法任意屡次。若要正确处理此种状况,必须让您的应用程序可以跟踪各个操做的完成。
SoundPlayer 和 PictureBox 组件表示基于事件的异步模式的简单实现。WebClient 和 BackgroundWorker 组件表示基于事件的异步模式的更复杂的实现。
下面是一个符合此模式的类声明示例:
public class AsyncExample { // Synchronous methods. public int Method1(string param); public void Method2(double param); // Asynchronous methods. public void Method1Async(string param); public void Method1Async(string param, object userState); public event Method1CompletedEventHandler Method1Completed; public void Method2Async(double param); public void Method2Async(double param, object userState); public event Method2CompletedEventHandler Method2Completed; public void CancelAsync(object userState); public bool IsBusy { get; } // Class implementation not shown. }
这里虚构的 AsyncExample 类有两个方法,都支持同步和异步调用。同步重载的行为相似于方法调用,它们对调用线程执行操做;若是操做很耗时,则调用的返回可能会有明显的延迟。异步重载将在另外一个线程上启动操做,而后当即返回,容许在调用线程继续执行的同时让操做“在后台”执行。
异步操做能够有两个重载:单调用和多调用。您能够经过方法签名来区分这两种形式:多调用形式有一个额外的参数,即 userState。使用这种形式,您的代码能够屡次调用 Method1Async(string param, object userState),而没必要等待任何挂起的异步操做的完成。另外一方面,若是您尝试在前一个调用还没有完成时调用 Method1Async(string param),该方法将引起InvalidOperationException。
多调用重载的 userState 参数可帮助您区分各个异步操做。您应分别为各个 Method1Async(string param, object userState) 调用提供一个惟一值(例如 GUID 或哈希代码);这样,当各个操做完成时,您的事件处理程序即可以肯定哪一个操做的实例引起了完成事件。
若是您使用多调用重载,您的代码将须要跟踪挂起的任务的 userState 对象(任务 ID)。对于每一个 Method1Async(string param, object userState) 调用,您一般应生成一个新的、惟一的userState 对象并将此对象添加到集合中。当对应于此 userState 对象的任务引起完成事件时,您的完成方法实现将检查 System.ComponentModel.AsyncCompletedEventArgs.UserState 并将此对象从集合中删除。在以这种方式使用时,userState 参数充当任务 ID 的角色。
![]() |
---|
在为您对多调用重载的调用中的 userState 提供惟一值时,必定要当心。若是任务 ID 不惟一,将致使异步类引起 ArgumentException。 |
咱们必须可以在异步操做完成以前随时取消它们,这一点很重要。实现基于事件的异步模式的类将有一个 CancelAsync 方法(若是有多个异步方法)或 MethodNameAsyncCancel 方法(若是只有一个异步方法)。
容许多个调用的方法采用 userState 参数,此参数可用来跟踪各个任务的生存期。CancelAsync 采用 userState 参数,此参数容许您取消特定的挂起任务。
一次只支持一个挂起的操做的方法(如 Method1Async(string param))是不可取消的。
符合基于事件的异步模式的类能够为跟踪进度和增量结果提供事件。此事件一般叫作 ProgressChanged 或 MethodNameProgressChanged,它对应的事件处理程序会带有一个ProgressChangedEventArgs 参数。
ProgressChanged 事件的事件处理程序能够检查 System.ComponentModel.ProgressChangedEventArgs.ProgressPercentage 属性来肯定异步任务完成的百分比。此属性的范围是 0 到 100,可用来更新 ProgressBar 的 Value 属性。若是有多个异步操做挂起,您可使用 System.ComponentModel.ProgressChangedEventArgs.UserState 属性来分辨出哪一个操做在报告进度。
一些类可能会在异步操做继续时报告增量结果。这些结果将保存的派生自 ProgressChangedEventArgs 的类中,并显示为此派生类中的属性。您能够在 ProgressChanged 事件的事件处理程序中访问这些结果,就像访问 ProgressPercentage 属性同样。若是有多个异步操做挂起,您可使用 UserState 属性来分辨出哪一个操做在报告增量结果。