了解异步方法,此示例会占用CPU时间吗?

时间:2016-03-10 17:02:28

标签: c#

我想更好地理解async - await模式。为此,我创建了以下功能

private async void SayHello()
{
    for(int i = 0; i < 100; i++)
    {
        await Task.Delay(1000);
        Console.Out.WriteLine("Hello");
    }
}

运行时,此功能打印&#34; Hello&#34;到了控制台100次显然。如果我理解正确,它就不会吃掉#34;通过卡在循环中一段时间​​的CPU时间。一旦执行到达async关键字,控制权就会传回给调用者并从那里继续。大约1秒后重新获得控制并发生打印。 这是正确的理解吗?

await语句完成之后,如何恢复控制权,无论它可能是什么?

此外,除了这个例子之外,有些await语句返回了一些值。我如何确保在调用SayHello的控制区域中完成此操作?

SayHello();
.. Do something
// Make sure the await in SayHello has returned

修改

如果我有FormOnLoad事件处理程序调用{​​{1}}以及SayHello点击时打印&#34;什么是& #34;到输出。为什么Button在加载时不会冻结100秒。当遇到Form时,似乎控制权被传递回SayHello的调用者。

await

1 个答案:

答案 0 :(得分:8)

  

如果我有一个带有OnLoad事件处理程序的Form,该事件处理程序调用SayHello以及一个Button,当点击时打印&#34;什么是&#34;&#39;到输出。

好的方案。让我们考虑一下这个;请注意,原始控制台方案中的工作原理略有不同。在我走的时候,我会做一些过度简化,但你会了解它的工作原理。

  

为什么Form在加载时不会冻结100秒。

魔术。

不,不是真的。

  

当遇到await时,似乎控制权被传递回SayHello的调用者。

正确。所以,让我们谈谈真正发生的事情。

事件处理程序并不神奇。事件发生时有人调用了该代码。这是怎么回事?

在调用堆栈的底部有一个你看不到的循环。该循环是您可以想象的最简单的代码。它从队列中提取消息,然后根据消息内容执行代码。由于显而易见的原因,我们将此称为&#34;消息循环&#34;

消息循环首先处理一个&#34;程序加载一个表单&#34;消息,因此表单加载处理程序运行。它有什么作用?它叫SayHello。 SayHello做了什么?

它创建一个对象,其中包含一个名为&#34; i&#34;的字段,将其设置为零,进入循环,调用Task.Delay,获取任务。

它创建一个以魔术对象作为接收者的委托;调用时该委托将在循环底部的writeline处继续执行该方法。称之为&#34;继续&#34;任务,因为接下来会发生什么。

委托作为其继续传递给任务,SayHello返回。

表单加载器返回。

现在消息循环再次运行。假设您单击一个按钮。按钮单击消息排队,最终消息处理器在队列中找到它,并运行单击处理程序。它返回。

假设延迟时间已过。延迟如何运作,谁知道,谁在乎,它是它的业务。不知何故,当延迟时间结束时,延迟代码会在消息队列中显示一条消息,说明&#34;嘿,任务已完成,请调用其继续&#34;。

最终消息队列绕过它,并调用continuation。延续是做什么的?它打印,设置&#34; i&#34;字段为1,检查循环条件,调用Task.Delay,为那个任务创建一个延续,嘿,我们再次完成所有操作。除非在等待await时,控制权将直接返回到消息循环。表单加载器早已不复存在。

魔法现在看起来有点神奇吗?神奇的是(1)有一个循环协调谁可以通过工作队列运行下一个,以及(2)编译器看到&#34; await&#34;并将整个方法转换为具有各种疯狂能力的特殊对象的特殊委托,例如在方法中间恢复执行的能力。 (试着猜测它是如何工作的;假设你必须自己编写代码来执行此操作;你怎么能这样做?)

现在您可能会问:消息循环是如何将CPU固定为100%的,如果其他任何时候都处于空闲状态,它会紧紧地坐在那里获取消息并分派它们。看看你是否能弄清楚它是如何运作的。

最后:控制台应用程序没有位于底部的消息循环,就像表单应用程序那样。你能推断出控制台应用程序如何安排延续,缺少消息循环吗?