异步/等待与进度/取消长时间运行的API方法 [英] Async/await for long-running API methods with progress/cancelation
问题描述
修改
我想强迫的await调用工人异步的正确方法是用一个Task.Run,像这样的:
Edit I suppose the proper way of forcing await to invoke the worker asynchronously is with a Task.Run, like this:
await Task.Run(() => builder.Build(dlg.FileName, cts.Token, new Progress(ReportProgress)));
得到 HTTP一些轻://博客。 msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx 。
这应该是容易的,但我是新来的异步/伺机让我难以承受。我建立一个类库公开的API与一些长期运行的操作。在过去,我用一个BackgroundWorker处理进展情况和取消的,想在这个简化的code片段:
this should be easy but I'm new to async/await so bear with me. I am building a class library exposing an API with some long-running operations. In the past, I used a BackgroundWorker to deal with progress reporting and cancelation, like in this simplified code fragment:
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);
}
}
和客户端code像:
BackgroundWorker worker = new BackgroundWorker
{ WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
worker.DoWork += new DoWorkEventHandler(_myWorkerClass.DoSomething);
worker.ProgressChanged += WorkerProgressChanged;
worker.RunWorkerCompleted += WorkerCompleted;
worker.RunWorkerAsync(someparam);
现在我想利用新的异步模式。所以,首先这里是我怎么会写在我的API一个简单的长时间运行的方法;在这里我只是逐行读取文件中的行,只是为了模拟真实世界的过程中,我将有一个转换文件格式有一些处理:
Now I'd like to leverage the new async pattern. So, first of all here is how I'd write a simple long-running method in my API; here I'm just reading a file line by line, just to emulate a real-world process where I'll have to convert a file format with some processing:
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类的方法。现在,这里是我的客户code(WPF的测试程序):
For the sake of this sample, say this is a method of a DummyWorker class. Now, here is my client code (a WPF test app):
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 ,所以你可以参考该URL。无论如何,在这种用法测试的UI被有效阻止,我看不出有什么进展。那么,什么将是这种情况的全貌,参照消费code?
The implementation for the IProgress interface comes from http://blog.stephencleary.com/2010/06/reporting-progress-from-tasks.html, so you can refer to that URL. Anyway, in this usage test the UI is effectively blocked and I see no progress. So what would be the full picture for such a scenario, with reference to the consuming code?
推荐答案
由于在该博客文章的顶部指出,在该职位信息已经过时。您应该使用新的 IProgress< T>
API提供了.NET 4.5
As noted on the top of that blog post, the information in that post is outdated. You should use the new IProgress<T>
API provided in .NET 4.5.
如果您使用的是阻塞I / O,然后让你的核心方法阻止:
If you're using blocking I/O, then make your core method blocking:
public void Build(string sInputFileName, CancellationToken cancel, IProgress<int> progress)
{
using (StreamReader reader = new StreamReader(sInputFileName))
{
int nLine = 0;
int nTotalLines = CountLines(sInputFileName);
while ((sLine = reader.ReadLine()) != null)
{
nLine++;
// do something here...
cancel.ThrowIfCancellationRequested();
if (progress != null) progress.Report(nLine * 100 / nTotalLines);
}
return nLine;
}
}
,然后把它包在 Task.Run
当你调用它:
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();
var progress = new Progress<int>((_, value) => { _progress.Value = value; });
await Task.Run(() => builder.Build(dlg.FileName, cts.Token, progress);
// hide the progress UI...
}
另外,你可以重写构建
来使用异步的API,然后就直接从事件处理程序中调用它无需 Task.Run <它包装/ code>。
Alternatively, you could rewrite Build
to use asynchronous APIs and then just call it directly from the event handler without wrapping it in Task.Run
.
这篇关于异步/等待与进度/取消长时间运行的API方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!