public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpGet]
public async Task<string> IndexAsync()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
await Delay(3);
await Delay(5);
await Delay(4);
stopwatch.Stop();
return stopwatch.Elapsed.Seconds.ToString();
}
private async Task<Int32> Delay(int sec)
{
await Task.Delay(1000 * sec);
return sec;
}
}
结果:
答案 0 :(得分:4)
许多人对await
的行为模棱两可。尽管有这个名字,他们还是以某种方式相信它开始。真相 1 。
await
在其右侧有一些表达。它不在乎该表达式是什么,它如何工作,等等。它所关心的只是表达式将产生 awaitable 2 。到目前为止,您将遇到的最常见的等待对象是Task
和Task<T>
。
然后await
完成简单的工作-这件事已经完成了吗?如果是这样,我们将得到结果(如果有),然后继续。 如果不是,那么我们将无法取得进一步的进展。如果其他人在此期间可以充分利用我们的线程,我们将让这种情况发生。待完成的事情完成后,我们安排了恢复执行包含await
的方法。
我只是重申-await
不在乎如何或为什么创建了等待的对象。那是别人的工作-在这里,它是由async
机器完成的,该机器改变了您的Delay
方法。但是await
不在乎该方法是否标记为async
(这是该方法的实现详细信息,尽管出现在其中,但不是其签名的一部分),只是它承诺会返回一个“热” Task
-已经运行的那个。
1 我认为这主要是因为有些人已经学会了近似相等async ≅ parallelism ≅ using threads
。从来都不是真的,但这就是我们一直在努力纠正的问题。因此,他们认为“ async
必须意味着我们正在创建线程”
2 的确,在您链接的文章中,Rawling
已经解决的问题那里表达式指的是以前由初始化的变量调用Task
返回方法。是什么使您自己的代码与众不同。
答案 1 :(得分:3)
在本文中,代码启动所有三个任务,然后唤醒所有三个任务:
var contentTask = service.GetContentAsync(); // start task 1
var countTask = service.GetCountAsync();
var nameTask = service.GetNameAsync();
var content = await contentTask; // wait until task 1 is finished
var count = await countTask;
var name = await nameTask;
您的代码开始并依次等待每个任务:
await Delay(3); // start task 1 and wait until it is finished
await Delay(5);
await Delay(4);
答案 2 :(得分:0)
进行以下代码修改,以使Async调用一起执行:
var result = await Task.WhenAll(Delay(3),Delay(4),Delay(5));
Task.WhenAll
将提供代表Task
,当提供的集合中的所有任务完成执行(成功/失败)时,代表await
将结束。因此,我们只是task
代表result
并获得了必要的行为,尽管它仍然可能无法保证预期的5秒钟。 int[]
将是Delay
,其中将包含每次调用Delay方法的值。
在您的情况下,await Task.Delay(1000 * sec);
方法负责使用Delay
启动任务,因此,如果您按已接受的答案所示调用await
,则分别{ {1}},它们仍然可以并行执行,但让我们假设,您不确定或Delay方法如下:
private Task Delay(int sec)
{
return Task.Delay(1000 * sec);
}
然后使用await Task.WhenAll(...)
变得很重要,因为它将启动尚未开始的任务,否则将仅等待它们完成。否则,使用接受的答案将不会获得好处。
有关C {6.0规范指南中的await
作为常规信息的更多信息:
await表达式的任务必须是可等待的。如果满足以下条件之一,则可以等待表达式t:
t具有一个名为GetAwaiter
的可访问实例或扩展方法,该方法或方法无参数且无类型参数,并具有以下所有条件的返回类型A:
System.Runtime.CompilerServices.INotifyCompletion
(以下称为
INotifyCompletion
为简洁起见 GetAwaiter
方法的目的是为任务获取等待者。 A型称为await表达式的awaiter类型。
IsCompleted
属性的目的是确定任务是否已完成。如果是这样,则无需暂停评估。
INotifyCompletion.OnCompleted
方法的目的是签署任务的“继续”;即,任务完成后将被调用的委托(类型为System.Action
)。
GetResult方法的目的是在任务完成后获得任务的结果。
此结果可能是成功完成,可能带有结果值,或者可能是GetResult方法抛出的异常。
表达式await t
的分类方式与表达式(t).GetAwaiter().GetResult()
相同。因此,如果返回类型为GetResult is void
,则await_expression
被归为空。如果它具有non-void return type T
,则await_expression
被归类为类型T
的值。
在运行时,表达式await t的计算如下:
(t).GetAwaiter()
获得一个等候者a 如果b为false,则评估取决于a是否实现了接口
System.Runtime.CompilerServices.ICriticalNotifyCompletion
(为简便起见,以下简称为ICriticalNotifyCompletion
)。此检查在绑定时完成;即在运行时,如果a的编译时类型为dynamic,否则在编译时。令r表示恢复委托(迭代器):
ICriticalNotifyCompletion
,则表达式
(a as (INotifyCompletion)).OnCompleted(r)
被评估。(a as (ICriticalNotifyCompletion)).UnsafeOnCompleted(r)
被评估。或者紧接(if b was true )
之后,或者稍后调用恢复委托(if b was false )
时,都会对表达式(a).GetResult()
进行求值。如果返回值,则该值是
await_expression。否则结果一无是处。
接口方法INotifyCompletion.OnCompleted
和ICriticalNotifyCompletion.UnsafeOnCompleted
的等待者实现应导致委托r最多被调用一次。否则,封闭的异步函数的行为是不确定的。