(我提前道歉,如果问题的某些部分/部分被认为是"太基本"。我已经完成了关于异步等待的研究,但对我而言,它不是一个容易掌握的概念。或者我没有找到合适的资源。)
我一直在阅读(或者就是我所解释的)"使同步异步呼叫同步的方法是等待'等待'在它面前"。
因此,此语句产生的代码如下所示:
set ASPNET_ENV=Debug
即:1)将// CODE 1:
public void async MyFunction()
{
await SomeFunctionAsync();
}
放在Async函数前面,2)将await
放在函数中,因为它现在包含async
。
但根据我的经验,有时会表现得有点儿,而且很有趣#34;我注意到的症状是,有时它的行为就像一个逃脱我控制的线程。
所以第一个问题将是:当您致电await
并且此功能包含MyFunction()
关键字时会发生什么?我的理解是(令人困惑的是)它不应该被称为async
,因为它是为返回MyFunctionAsync()
的函数保留的。
...所以我提出的替代方案(似乎表现得像一个同步类一样)是这种类型的代码:
Task
第二个问题:有人可以解释// CODE 2:
public void MyFunction()
{
var task = SomeFunctionAsync();
Task.WaitAll(task);
}
的缺点(如果有的话)吗?
此外,隐含的问题是我有兴趣知道我可能对async-await模式有任何误解。
答案 0 :(得分:4)
我一直在阅读(或者就是我所解释的)“使异步调用同步的方法是将'等待'放在它前面”。
完全没有。这是一种串行方式来调用异步代码,但不是同步。 “同步”表示“阻止当前线程”,await
不会阻止当前线程。但是,“序列”表示“在下一件事之前执行此操作”,await
将执行此操作。有关详细信息,请参阅我的async
intro。
使用await
是调用异步方法最自然和最常用的方法。请注意,通常在向方法添加async
时,也应将返回类型从void
更改为Task
。编译器将指导您:如果您只是将await
放在async
而没有async void
,它将非常明确地为您建议下一个更改。
但根据我的经验,有时表现得有点“有趣”。我注意到的症状是,它有时像一个逃脱我控制的线程。
这是因为您的代码使用的是async Task
而不是async void
。 Task
不自然;你没有“控制权”,因为你没有返回async Task
。更自然的方法是使方法await
和async
来自它的调用者的任务,等等。 MyFunction
通过你的堆栈“成长”是很自然的 - 我们称之为“async all the way”。
所以第一个问题是:当你调用MyFunction()并且这个函数包含async关键字时会发生什么?我的理解是(令人困惑的是)它不应该被称为MyFunctionAsync(),因为它是为返回任务的函数保留的。
您对命名的理解是正确的。 Task
不是一种自然的(async void
- 返回)异步方法。
async
方法非常奇怪,并且不像普通await
方法那样。他们奇怪的行为是我推荐给avoid async void
的一个原因。但既然你问过......
async void
方法中的await
就像常规async
方法中的async
一样(正如我在SynchronizationContext
介绍中所描述的那样):将捕获当前上下文(当前TaskScheduler
或async void
,并在异步操作完成时继续在该上下文中执行void
方法。这就是为什么它就像一个“线程之外的线程”控制“ - 它只是在需要时恢复执行。你无法检测它何时完成,因为该方法返回Task
而不是async void
。
例外是事情变得更加奇怪。 SynchronizationContext
方法在执行开始时捕获当前async void
,如果异常转义SynchronizationContext
方法,它将在其捕获的AggregateException
上重新引发该异常。这通常会导致进程级崩溃。
第二个问题:有人可以解释CODE 2的缺点(如果有的话)吗?
它很容易导致我在博客上描述的deadlock situation。它还将在{{1}}。
中包装任务中的任何异常底线是:不要阻止异步代码。无论如何你要阻止它,那么为什么要首先使它异步?任何混合的阻塞和异步代码都应被视为具有相当高优先级的技术债务。没有适用于所有情况的解决方案,但是在将代码升级为正确异步时,您可以临时使用各种黑客;我在brownfield async development的一篇文章中描述了它们。