Async await 关键字是否等同于 ContinueWith lambda?

Is Async await keyword equivalent to a ContinueWith lambda?

提问人:Maxim Gershkovich 提问时间:1/7/2012 最后编辑:David KlempfnerMaxim Gershkovich 更新时间:9/14/2021 访问量:22113

问:

有人可以确认我是否正确理解了 Async await 关键字吗?(使用CTP第3版)

到目前为止,我已经发现在方法调用之前插入 await 关键字基本上可以做两件事,A。它创造了一个即时的回报和B。它创建一个“延续”,在异步方法调用完成后调用该“延续”。在任何情况下,延续都是该方法代码块的其余部分。

所以我想知道的是,这两段代码在技术上是否等效,如果是这样,这是否意味着 await 关键字与创建 ContinueWith Lambda 相同(即:它基本上是一个编译器快捷方式)?如果不是,有什么区别?

bool Success =
    await new POP3Connector(
        "mail.server.com", txtUsername.Text, txtPassword.Text).Connect();
// At this point the method will return and following code will
// only be invoked when the operation is complete(?)
MessageBox.Show(Success ? "Logged In" : "Wrong password");

(new POP3Connector(
    "mail.server.com", txtUsername.Text, txtPassword.Text ).Connect())
.ContinueWith((success) =>
    MessageBox.Show(success.Result ? "Logged In" : "Wrong password"));
C# async-await 延续

评论


答:

9赞 James Manning 1/7/2012 #1

“本质上”就是这样,但生成的代码严格来说远不止于此。有关生成的代码的更多详细信息,我强烈推荐 Jon Skeet 的 Eduasync 系列:

http://codeblog.jonskeet.uk/category/eduasync/

特别是,帖子 #7 介绍了生成的内容(从 CTP 2 开始)以及原因,因此可能非常适合您目前正在寻找的内容:

http://codeblog.jonskeet.uk/2011/05/20/eduasync-part-7-generated-code-from-a-simple-async-method/

编辑:我认为它可能比您从问题中寻找的内容更详细,但是如果您想知道当您在方法中有多个等待时事情会是什么样子,那么#9 帖子中涵盖了:)

http://codeblog.jonskeet.uk/2011/05/30/eduasync-part-9-generated-code-for-multiple-awaits/

86赞 Stephen Cleary 1/7/2012 #2

总体思路是正确的 - 该方法的其余部分被制成各种延续。

“快速路径”博客文章详细介绍了 / 编译器转换的工作原理。asyncawait

差异,从我的头顶:

该关键字还使用了“调度上下文”概念。调度上下文是 如果存在,则回退到 。然后,在调度上下文上运行延续。因此,更接近的近似值是传递到 ,必要时回退。awaitSynchronizationContext.CurrentTaskScheduler.CurrentTaskScheduler.FromCurrentSynchronizationContextContinueWithTaskScheduler.Current

实际/实现基于模式匹配;它使用“可等待”模式,允许等待任务以外的其他事情。一些示例包括 WinRT 异步 API、一些特殊方法(如 Rx 可观察对象)和特殊的套接字可调试对象,这些方法不会对 GC 造成太大影响。任务很强大,但它们并不是唯一等待的。asyncawaitYield

我想到了一个细微的挑剔差异:如果 awaitable 已经完成,那么该方法实际上不会在此时返回;它同步继续。所以这有点像 pass ,但没有与堆栈相关的问题。asyncTaskContinuationOptions.ExecuteSynchronously

评论

2赞 James Manning 1/8/2012
说得好 - 我尽量听从乔恩的帖子,因为它们比我有时间在 SO 上回答的任何内容都要广泛得多,但斯蒂芬是绝对正确的。WRT 什么是等待的(尤其是 GetAwaiter),他的帖子 #3 非常有帮助恕我直言:)msmvps.com/blogs/jon_skeet/archive/2011/05/13/......
5赞 Theo Yaung 1/8/2012
斯蒂芬在这里的位置。举个简单的例子,很容易认为 async/await 只是 ContinueWith 的快捷方式 - 但是,我喜欢反过来思考它。Async/await 实际上是您过去使用 ContinueWith 的更强大的表达方式。问题在于 ContinueWith(...) 使用 lambda,并允许将执行转移到延续,但如果您必须将循环体的一半放在 ContinueWith(...) 之前,而将另一半放在 ContinueWith(...) 之后,则其他控制流概念(例如循环)几乎是不可能的。您最终会得到手动延续链接。
8赞 Theo Yaung 1/8/2012
async/await 比 ContinueWith(...) 更具表现力的另一个示例是异常流。您可以在同一个 try 块中多次等待,对于执行的每个阶段,它们的异常可以汇集到同一个 catch(...) 块,而无需编写大量显式执行此操作的代码。
4赞 Theo Yaung 1/8/2012
The last portion of async/await that's notable is that it's a "higher level concept" whereas ContinueWith(...) is more manual and explicitly has lambdas, delegate creations, etc. With higher level concepts there's more opportunity for optimization - so for example, multiple awaits in the same method actually "share" the same lambda closure (it's like the overhead of a single lambda), whereas ContinueWith(...) gets the overhead each time you invoke it, because you explicitly wrote a lambda, so the compiler gives that to you.
1赞 Stephen Cleary 5/9/2020
@MobyDisk: To clarify, still captures just like it always has. But on ASP.NET Core, is .awaitSynchronizationContext.CurrentSynchronizationContext.Currentnull