C# await 和 ContinueWith 之间的区别

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18965200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 13:43:25  来源:igfitidea点击:

Difference between await and ContinueWith

c#task-parallel-librarytaskasync-await

提问by Harrison

Can someone explain if awaitand ContinueWithare synonymous or not in the following example. I'm trying to use TPL for the first time and have been reading all the documentation, but don't understand the difference.

有人可以解释以下示例中awaitContinueWith是否同义。我是第一次尝试使用 TPL 并且已经阅读了所有文档,但不明白其中的区别。

Await:

等待

String webText = await getWebPage(uri);
await parseData(webText);

ContinueWith:

继续

Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) =>  parseData(task.Result));
webText.Start();
continue.Wait();

Is one preferred over the other in particular situations?

在特定情况下,一个比另一个更受欢迎吗?

采纳答案by Jon Skeet

In the second code, you're synchronouslywaiting for the continuation to complete. In the first version, the method will return to the caller as soon as it hits the first awaitexpression which isn't already completed.

在第二个代码中,您同步等待继续完成。在第一个版本中,该方法会在遇到第一个await尚未完成的表达式时立即返回给调用者。

They're very similar in that they both schedule a continuation, but as soon as the control flow gets even slightly complex, awaitleads to muchsimpler code. Additionally, as noted by Servy in comments, awaiting a task will "unwrap" aggregate exceptions which usually leads to simpler error handling. Also using awaitwill implicitly schedule the continuation in the calling context (unless you use ConfigureAwait). It's nothing that can't be done "manually", but it's a lot easier doing it with await.

他们是,它们都计划延续很相似,但只要控制流变得稍微复杂,await导致很多简单的代码。此外,正如 Servy 在评论中所指出的,等待任务将“解开”聚合异常,这通常会导致更简单的错误处理。此外 usingawait将隐式安排调用上下文中的延续(除非您使用ConfigureAwait)。没有什么不能“手动”完成的,但是使用await.

I suggest you try implementing a slightly larger sequence of operations with both awaitand Task.ContinueWith- it can be a real eye-opener.

我建议您尝试同时使用await和执行稍微大一点的操作序列Task.ContinueWith- 这可能会让您大开眼界。

回答by pkt

Here's the sequence of code snippets I recently used to illustrate the difference and various problems using async solves.

这是我最近用来说明使用异步解决的差异和各种问题的代码片段序列。

Suppose you have some event handler in your GUI-based application that takes a lot of time, and so you'd like to make it asynchronous. Here's the synchronous logic you start with:

假设您的基于 GUI 的应用程序中有一些需要大量时间的事件处理程序,因此您希望使其异步。这是您开始的同步逻辑:

while (true) {
    string result = LoadNextItem().Result;
    if (result.Contains("target")) {
        Counter.Value = result.Length;
        break;
    }
}

LoadNextItem returns a Task, that will eventually produce some result you'd like to inspect. If the current result is the one you're looking for, you update the value of some counter on the UI, and return from the method. Otherwise, you continue processing more items from LoadNextItem.

LoadNextItem 返回一个任务,它最终会产生一些你想要检查的结果。如果当前结果是您要查找的结果,则更新 UI 上某个计数器的值,然后从该方法返回。否则,您将继续处理来自 LoadNextItem 的更多项目。

First idea for the asynchronous version: just use continuations! And let's ignore the looping part for the time being. I mean, what could possibly go wrong?

异步版本的第一个想法:只使用延续!让我们暂时忽略循环部分。我的意思是,可能会出现什么问题?

return LoadNextItem().ContinueWith(t => {
    string result = t.Result;
    if (result.Contains("target")) {
        Counter.Value = result.Length;
    }
});

Great, now we have a method that does not block! It crashes instead. Any updates to UI controls should happen on the UI thread, so you will need to account for that. Thankfully, there's an option to specify how continuations should be scheduled, and there's a default one for just this:

太好了,现在我们有了一个不会阻塞的方法!它反而崩溃了。UI 控件的任何更新都应该在 UI 线程上进行,因此您需要考虑到这一点。值得庆幸的是,有一个选项可以指定如何安排延续,并且有一个默认选项:

return LoadNextItem().ContinueWith(t => {
    string result = t.Result;
    if (result.Contains("target")) {
        Counter.Value = result.Length;
    }
},
TaskScheduler.FromCurrentSynchronizationContext());

Great, now we have a method that does not crash! It fails silently instead. Continuations are separate tasks themselves, with their status not tied to that of the antecedent task. So even if LoadNextItem faults, the caller will only see a task that has successfully completed. Okay, then just pass on the exception, if there is one:

太好了,现在我们有了一个不会崩溃的方法!它反而默默地失败了。延续本身是独立的任务,其状态与先前任务的状态无关。因此,即使 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());

Great, now this actually works. For a single item. Now, how about that looping. Turns out, a solution equivalent to the logic of the original synchronous version will look something like this:

太好了,现在这确实有效。对于单个项目。现在,循环怎么样。原来,与原始同步版本的逻辑等效的解决方案将如下所示:

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);
        }});
}

Or, instead of all of the above, you can use async to do the same thing:

或者,你可以使用 async 来做同样的事情,而不是上面所有的:

async Task AsyncLoop() {
    while (true) {
        string result = await LoadNextItem();
        if (result.Contains("target")) {
            Counter.Value = result.Length;
            break;
        }
    }
}

That's a lot nicer now, isn't it?

现在好多了,不是吗?