下面是我最近用来说明使用异步解决方案的差异和各种问题的代码片段序列。
假设您在基于GUI的应用程序中有一些事件处理程序花费了大量时间,因此您希望使其处于异步。这里是你开始的同步逻辑:
while (true) {
string result = LoadNextItem().Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
LoadNextItem返回一个Task,它最终会产生一些你想检查的结果。如果当前结果是您正在查找的结果,则更新UI上某个计数器的值,然后从该方法返回。否则,您将继续处理来自LoadNextItem的更多项目。
异步版本的第一个想法:只使用continuations!让我们暂时忽略循环部分。我的意思是,什么可能会出错?
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
});
太棒了,现在我们有一种方法不会阻塞!它崩溃了。 UI控件的任何更新都应该在UI线程上发生,因此您需要对此进行说明。值得庆幸的是,有指定的延续应该如何安排的选项,并有一个默认的只是这一点:
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
太好了,现在我们有一个不死机的方法!它反而失败了。继续是他们自己的独立任务,他们的地位与先前的任务没有关系。因此,即使LoadNextItem发生故障,调用者也只会看到已成功完成的任务。好吧,那只是传递异常时,如果有一个:
return LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
throw t.Exception.InnerException;
}
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
太好了,现在该实际工作。对于单个项目。现在,那个循环如何?事实证明,解决方案相当于原来的同步版本的逻辑将是这个样子:
Task AsyncLoop() {
return AsyncLoopTask().ContinueWith(t =>
Counter.Value = t.Result,
TaskScheduler.FromCurrentSynchronizationContext());
}
Task<int> AsyncLoopTask() {
var tcs = new TaskCompletionSource<int>();
DoIteration(tcs);
return tcs.Task;
}
void DoIteration(TaskCompletionSource<int> tcs) {
LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
tcs.TrySetException(t.Exception.InnerException);
} else if (t.Result.Contains("target")) {
tcs.TrySetResult(t.Result.Length);
} else {
DoIteration(tcs);
}});
}
或者,而不是所有上述情况,你可以使用异步做同样的事情:
async Task AsyncLoop() {
while (true) {
string result = await LoadNextItem();
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
}
现在好多了,不是吗?
来源
2013-09-24 13:10:16
pkt
如果您删除在第二个例子中'Wait'通话*然后*这两个片段会(大部分)等同。的 – Servy
可能重复的[是异步等待关键字相当于ContinueWith拉姆达?](http://stackoverflow.com/questions/8767218/is-async-await-keyword-equivalent-to-a-continuewith-lambda) –
供参考:您的'getWebPage'方法不能在两个代码中使用。在第一个代码中它有一个'Task'返回类型,而在第二个代码中它有'string'返回类型。所以基本上你的代码不能编译。 - 如果要精确。 –