等待和ContinueWith之间的区别

有人可以解释,如果awaitContinueWith是同义或不在下面的例子。 我正在尝试第一次使用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(); 

在特定的情况下比其他人更喜欢吗?

在第二个代码中,您正在同步等待继续完成。 在第一个版本中,只要它遇到尚未完成的第一个awaitexpression式,方法就会返回给调用者。

他们是非常相似的,他们都安排了一个延续,但只要控制stream程稍微复杂一点, await会导致简单的代码。 此外,正如Servy在评论中指出的那样,等待任务将“解包”聚合exception,这通常会导致更简单的error handling。 同样使用await会隐式地调用调用上下文中的延续(除非使用ConfigureAwait )。 这不是“手动”无法完成的,但是在await更容易。

我build议你试着用awaitTask.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; } } } 

现在好多了,不是吗?