等待和ContinueWith之间的区别
有人可以解释,如果await
和ContinueWith
是同义或不在下面的例子。 我正在尝试第一次使用TPL,并一直在阅读所有的文档,但不明白其中的差别。
等待 :
String webText = await getWebPage(uri); await parseData(webText);
继续 :
Task<String> webText = new Task<String>(() => getWebPage(uri)); Task continue = webText.ContinueWith((task) => parseData(task.Result)); webText.Start(); continue.Wait();
在特定的情况下比其他人更喜欢吗?
在第二个代码中,您正在同步等待继续完成。 在第一个版本中,只要它遇到尚未完成的第一个await
expression式,方法就会返回给调用者。
他们是非常相似的,他们都安排了一个延续,但只要控制stream程稍微复杂一点, await
会导致更简单的代码。 此外,正如Servy在评论中指出的那样,等待任务将“解包”聚合exception,这通常会导致更简单的error handling。 同样使用await
会隐式地调用调用上下文中的延续(除非使用ConfigureAwait
)。 这不是“手动”无法完成的,但是在await
更容易。
我build议你试着用await
和Task.ContinueWith
来执行一个稍微大一点的操作 – 它可以是一个真正的大开眼界。
下面是我最近用来说明使用asynchronous解决scheme的差异和各种问题的代码片段序列。
假设在基于GUI的应用程序中有一些事件处理程序花费了大量的时间,所以您希望将其设置为asynchronous。 以下是您开始的同步逻辑:
while (true) { string result = LoadNextItem().Result; if (result.Contains("target")) { Counter.Value = result.Length; break; } }
LoadNextItem返回一个Task,最终会产生你想要检查的结果。 如果当前结果是您正在查找的结果,则更新UI上某个计数器的值,然后从方法返回。 否则,您将继续处理来自LoadNextItem的更多项目。
asynchronous版本的第一个想法:只使用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());
太好了,现在这个真的有用了 对于单个项目。 现在,这个循环如何? 事实certificate,与原始同步版本的逻辑等效的解决scheme将如下所示:
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); }}); }
或者,也可以使用asynchronous来完成同样的任务:
async Task AsyncLoop() { while (true) { string result = await LoadNextItem(); if (result.Contains("target")) { Counter.Value = result.Length; break; } } }
现在好多了,不是吗?