给出方法签名:
(awaitable) Task<Token> ITokenService.GetAPIToken();
在这种方法中,与await
一起发生的事情是什么?
public async Task<User> GetUser(string userId)
{
Token token = await TokenService.GetAPIToken();
//..........
}
我的理解:调用GetAPIToken
,当前方法(GetUser
)返回一个Task<User>
对象(将提供一个User
对象后来)。只有GetAPIToken
方法返回后,当前方法的执行才会继续。
如果我错了,那么代码等待token
方法返回时GetAPIToken()
的类型/值是什么?
我知道调用此方法的结果会有所不同,因为可以使用或不使用await
关键字来调用此方法。对于该帖子,请假定该帖子是通过await
调用的。
答案 0 :(得分:7)
我的理解:调用
GetAPIToken
,当前方法(GetUser
)返回一个Task<User>
对象(稍后将提供一个User对象)。只有GetAPIToken
方法返回后,当前方法的执行才会继续。
那非常接近。让我们用两种方法来完善它。
首先,让我们区分 returns (这意味着将控制权返回给调用者)和 completes ,这意味着该方法所代表的任务已完成并且我们有它的价值,或者在异常完成的情况下有例外。
在非异步方法中,我们无需进行区分,因为仅在完成时才将控制权返回给调用方。在异步方法中,我们可以暂停返回(由于等待未完成的任务而调用方)或完成返回(当表示的任务被信号通知完成时)。
第二,用任务创建时完成的情况来表达工作流是有帮助的。例如,在缓存的情况下可能会发生这种情况。
现在让我们重新考虑一下您的理解:
GetAPIToken
被调用并将任务返回给当前用户。Task<User>
GetUser
将GetUser
的其余部分分配为Task<Token>
的延续,并通过返回Task<User>
Task<Token>
完成后,它将继续运行,并从中断的地方继续GetUser
。当代码等待GetAPIToken()方法返回时,令牌的类型/值是什么?
那么,在等效的非异步情况下,token
的值是什么?考虑:
Token x = Foo();
我们在等待Foo完成时x的值是多少?使Foo异步并没有什么不同;在呼叫正常完成之后,本地才会被分配值。如果Foo陷入无限循环,或者Foo抛出,则永远不会分配x
。
当然,在实践中,C#在创建所有本地变量时为其分配默认值,因此这就是变量中的值。
答案 1 :(得分:1)
在与await
的行上,实际上GetUser()
的执行已停止(唤醒),直到GetAPIToken()
返回。这是对的。因此,token
在返回GetAPIToken()
之前没有任何价值(或者在这种情况下甚至被声明)。
GetUser()
返回的内容取决于调用它的方式,带有或不带有await
运算符。如果使用await
进行了调用,则GetUser()
的调用者也将等待它完成。同样在这种情况下,呼叫者最终将得到User
返回的GetUser()
,而不是Task<User>
。
在这种情况下,并行运行任何内容都不会使您受益。
如果另一方面,GetUser()
是在没有await
运算符的情况下被调用的,则发生了两种不同的情况:GetUser()
早在实际到达您的线路时就返回到调用方;并在所有内容都未创建之后直接返回Task<User>
而不是User
。
调用者可以稍后从此Task
(在并行完成其他事情之后,这就是重点)得到结果User
。这可以通过不同的方式来完成。请参阅Task<>
的方法和属性。
了解并行运行的时间很重要。在这种情况下(如果在没有GetUser()
的情况下调用await
的情况下,有可能GetUser()
返回并且您继续做事,而同时在另一个线程中GetAPIToken()
还没有还没有返回,因此token
未初始化。
使用图表检查此不错的MSDN文章: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/index#BKMK_WhatHappensUnderstandinganAsyncMethod