编辑 我认为强制 await 异步调用 worker 的正确方式是使用 Task.Run,像这样:
await Task.Run(() => builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress)));
从http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx中得到了一些启示。
这应该很容易,但我对async/await很陌生,请耐心等待。我正在构建一个公开API的类库,其中包含一些长时间运行的操作。过去,我使用BackgroundWorker来处理进度报告和取消,就像这个简化的代码片段:
public void DoSomething(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = (BackgroundWorker)sender;
// e.Argument is any object as passed by consumer via RunWorkerAsync...
do
{
// ... do something ...
// abort if requested
if (bw.CancellationPending)
{
e.Cancel = true;
break;
} //eif
// notify progress
bw.ReportProgress(nPercent);
}
}
客户端代码如下:
BackgroundWorker worker = new BackgroundWorker
{ WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
worker.DoWork += new DoWorkEventHandler(_myWorkerClass.DoSomething);
worker.ProgressChanged += WorkerProgressChanged;
worker.RunWorkerCompleted += WorkerCompleted;
worker.RunWorkerAsync(someparam);
现在我想利用新的异步模式。首先,这是我如何在我的API中编写一个简单的长时间运行的方法;在这里,我只是逐行读取文件,以模拟一个需要转换文件格式并进行一些处理的真实流程:
public async Task DoSomething(string sInputFileName, CancellationToken? cancel, IProgress progress)
{
using (StreamReader reader = new StreamReader(sInputFileName))
{
int nLine = 0;
int nTotalLines = CountLines(sInputFileName);
while ((sLine = reader.ReadLine()) != null)
{
nLine++;
// do something here...
if ((cancel.HasValue) && (cancel.Value.IsCancellationRequested)) break;
if (progress != null) progress.Report(nLine * 100 / nTotalLines);
}
return nLine;
}
}
为了这个示例,假设这是DummyWorker类的一个方法。现在,这是我的客户端代码(一个WPF测试应用程序):
private void ReportProgress(int n)
{
Dispatcher.BeginInvoke((Action)(() => { _progress.Value = n; }));
}
private async void OnDoSomethingClick(object sender, RoutedEventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog { Filter = "Text Files (*.txt)|*.txt" };
if (dlg.ShowDialog() == false) return;
// show the job progress UI...
CancellationTokenSource cts = new CancellationTokenSource();
DummyWorker worker = new DummyWorker();
await builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress));
// hide the progress UI...
}
IProgress接口的实现代码来自于http://blog.stephencleary.com/2010/06/reporting-progress-from-tasks.html,你可以参考该网址。无论如何,在这个应用测试中,用户界面被有效地阻塞,我看不到任何进展。因此,在这种情况下,与消费者代码相关的完整图像是什么?
Task.Run()
是实现这个的方法。或者如果操作确实很长,可能需要使用Task.Factory.StartNew()
并设置LongRunning
选项。 - svick