使用异步Lambda表达式
到目前为止,本章只介绍了异步方法。但我们曾经说过,你还可以使用异步匿名方法和异步
Lambda表达式。这些构造尤其适合那些只有少量工作要做的事件处理程序。下面的代码片段将
一个表达式注册为一个按钮点击事件的事件处理程序。
startWorkButton.Click+=async (sender, e) =>
{
//处理点击处理程序工作
};
下面用一个简短的WPF程序来展示其用法,下面为后台代码:
using System.Threading.Tasks;
using System.Windows;
namespace AsyncLambda
{
public partical class MainWindow:Window
{
public MainWindow()
{
InitializeComponent();
startWorkButton.Click+=async(sender,e)=>
{
SetGuiValues(false,"Work Started");
await DoSomeWork();
SetGuiValues(true,"Work Finished");
};
}
private void SetGuiValues(bool buttonEnabled,string status)
{
startWorkButton.IsEnabled=buttonEnabled;
workStartedTextBox.Text=status;
}
private Task DoSomeWork()
{
return Task.Delay(2500);
}
}
}
XAML文件中的标记如下:
<Window x:Class="AsyncLambda.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Async lambda" Height="115" Width="150">
<StackPanel>
<TextBlock Name="workStartedTextBlock" Margin="10,10"/>
<Button Name="startWorkButton" Width="100" Margin="4" Content="Start Work" />
</StackPanel>
图21-13展示了这段程序生成的窗体的三种状态。
BackgroundWorker类
前面几节介绍了如何使用async/await特性来异步地处理任务。本节将学习另一种实现异步
工作的方式,即后台线程。async/await特性更适合那些需要在后台完成的不相关的小任务。
但有时候,你可能需要另建一个线程,在后台持续运行以完成某项工作,并不时地与主线程
通信。BackgroundWorker类就是为此而生的。图21-15展示了该类的主要成员。
## **BackgroundWorker类主要成员**
- 属性
- WorkerReportsProgress(R/W)
- WorkerSupportsCancellation(R/W)
- IsBusy(R)
- CancellationPending(R)
- 方法
- RunWorkerAsync()
- CancelAsync()
- ReportProgress()
- 事件
- DoWork
- ProgressChanged
- RunWorkerCompleted
- 图标说明(补充)
- R:Read
- R/W:Read/Write
- Event:事件标识
- 带阴影块:Called from Worker
图中的前两个属性用于设置后台任务是否可以把它的进度汇报给主线程以及是否支持从
主线程取消。可以用第三个属性来检查后台任务是否正在运行。类有3个事件,用于发送不同的程序事件和状态。你需要写这些事件的事件处理方法来
执行适合程序的行为。- 在后台线程开始的时候触发Do№工k。
- 在后台任务汇报进度的时候触发ProgressChanged事件。
- 在后台工作线程退出的时候触发RunWorkerComp1eted事件。
三个方法用于开始行为或改变状态。
- 调用RunWorkerAsync方法获取后台线程并且执行DoWork事件处理程序。
- 用CancelAsync方法把CancellationPending属性设置为true。DoWork事件处理程序
需要检查这个属性来决定是否应该停止处理。 - DoWork事件处理程序(在后台线程)在希望向主线程汇报进度的时候,调用ReportProgress
方法。
要使用BackgroundWorker类对象,需要写如下的事件处理程序。第一个是必需的,因为它包
含你希望在后台线程执行的代码。另外两个是可选的,是否使用取决于程序需要。
附加到DoWork事件的处理程序包含你希望在后台独立线程上执行的代码。
在图21·16中,叫作DoTheWork的处哩程序用渐变的方块表示,表明它在后台线程中执行。- 主线程调用BackgroundWorker对象的RunWorkerAsync方法的时候会触发DoWork事件。
这个后台线程通过调用ReportProgress方法与主线程通信。届时将触发ProgressChanged
事件,主线程可以用附加到ProgressChanged事件上的处理程序处理事件。附加到RunWorkerComp1eted事件的处理程序应该包含在后台线程完成DoWork事件处理程
序的执行之后需要执行的代码。
图21-16演示了程序的结构,以及附加到对象事件的事件处理程序。
这些事件处理程序的委托如下。每一个任务都接受一个object对象的引用作为第一个参数,
以及EventArgs类的特定子类作为第二个参数。
void DoWorkEventhandler(object sender,DoWorkEventArge e)
void ProgressChangedEventHandler(object sender,ProgressChangedEventArgs e)
void RunWorkerCompletedEventHandler(object sender,RunWorkerCompletedEventArgs e)
如果你编写了事件处理程序并将其附加到相应的事件,就可以使用这个类。
- 从创建BackgroundWorker类的对象并且对它进行配置开始。
- 如果希望工作线程向主线程汇报进度,需要把WorkerReportsProgress属性设置为true。
- 如果希望从主线程取消工作线程,就把workersupportscancellation属性设置为trueo
- 既然对象已经配置好了,就可以通过调用RunWorkerAsync方法来启动它。这会获取一个
后台线程,触发Dowork事件并在后台执行事件处理程序。
现在我们已经运行了主线程以及后台线程。尽管后台线程正在运行,你仍然可以继续主线程
上的处理。
在主线程中,如果你已经启用了workersupportscancellation属性,就可以调用对象的CancelAsync
方法。和本章开头介绍的CancellationToke一样,它也不会取消后台线程,而是将对象的
CancellationPending属性设置为true。在后台线程中运行的DoWork事件处理程序代码需要定期
检查CancellationPending属性,来判断是否需要退出。
同时,后台线程继续执行其计算任务,并且做以下几件事情。
- 如果WorkerReportsProgress属性是true并且后台线程需要向主线程汇报进度的话,它必
须调用Backgroundldorker对象的ReportProgress方法。这会触发主线程的ProgressChanged
事件,从而运行相应的事件处理程序。 - 如果workersupportscancellation属性启用的话,DoWork事件处理程序代码应该定期检
查CancellationPending属性来确定它是否已经取消了。如果是的话,则它应该退出。 - 如果后台线程没有取消,而是完成了其处理的话,可以通过设置DoWorkEventArgs参数的
Result字段来返回结果给主线程,如图21-17所示。
在后台线程退出的时候会触发RunWorkerCompleted事件,其事件处理程序在主线程上执行。
RunWorkerCompletedEventArgs参数可以包含已完成后台线程的一些信息,比如返回值以及线程是
否被取消了。