这是我在理解async-await时的最后一次尝试。我对么?

时间:2016-01-11 03:15:55

标签: c# .net asynchronous async-await

我想我终于在以下段落之后理解了它

  

如果使用或async修饰符标记方法,则可以使用await   方法中的运算符。当控件到达await表达式时   异步方法,控制返回调用者,并在进度中   方法暂停,直到等待的任务完成。当任务是   完成后,执行可以在方法中恢复。

来自http://blogs.msdn.com/b/csharpfaq/archive/2012/06/26/understanding-a-simple-async-program.aspx

所以,让我说我有一个功能

public void Caller ( ) 
{
    Something1();
    SomeAsyncMethod();
    Something2();
    Something3();
    Something4();
}

public async void SomeAsyncMethod ( )
{
    Task<string> getWebPageTask = GetWebPageAsync("http://stackoverflow.com");
    Console.WriteLine("Begin");
    string task = await getWebPageTask;
    Console.WriteLine("End");
    // return 
}

为了论证,让我们说明完成task排队所需的时间我在下面指定的地方

    Something2();
    Something3();
    // task would be done at this point
    Something4();

然后执行流程实际上就像

    Something1();
    Task<string> getWebPageTask = GetWebPageAsync("http://stackoverflow.com");
    Console.WriteLine("Begin");
    Something2();
    Something3();
    string task = [ string returned by GetWebPageAsync("http://stackoverflow.com") ] 
    Console.WriteLine("End");
    Something4();

所以它很像yield语句,因为执行在方法的调用者和方法体之间切换。

我现在能理解吗?

2 个答案:

答案 0 :(得分:10)

  

我现在能理解吗?

不,不完全。完成getWebPageTask中存储的任务不会导致当前运行的线程Something3丢弃它正在执行的操作并运行剩余的SomeAsyncMethod。 (除非有一些真正奇怪的事情发生,比如其中一种方法会引发消息循环。)

相反,延续计划在未来某个未指定的时间运行。

这是如何工作的?我们假设您使用的是Windows窗体应用程序,因为这很容易理解。您可能已经注意到,在winforms应用程序中,所有内容都是&#34;事件驱动&#34;。也就是说,当事件发生时,事件处理程序会以某种方式神秘地执行。这不是魔术。顶部有一个循环,它将消息队列中的窗口消息拉出,解码它们,并确定是否有与之关联的事件处理程序。如果有则处理程序运行;它只是一个普通的方法调用。

当任务在Windows窗体应用程序中完成时,它会在队列中放入一条消息,表示刚刚发生任务完成事件;处理此消息时,请调用此延续&#34;。

这就是为什么将来会在某个时间调用延期 - 消息循环不会再次运行,直到它刚调用的所有代码为止返回它。如果任务完成,那么该消息将位于队列中以便按顺序处理。

现在,其他控制流程也是可能的。您可以配置一个线程,以便当该线程启动的任务完成时,完成计划在将来在任务管理器选择的工作线程上运行。但是,在我们跑步之前,让我们走吧;在尝试弄清楚它是如何在多个线程上工作之前,请确保您了解一个线程的工作方式。

假设消息循环调用了您的Caller方法,单线程异步程序中的实际控制流将如下所示:

message loop invokes Caller
    Caller invokes Something1
        Something1 returns 
    Caller invokes SomeAsyncMethod
        SomeAsyncMethod invokes GetWebPageAsync
            GetWebPageAsync starts fetching a web page and returns a task
        SomeAsyncMethod stores the task
        SomeAsyncMethod writes Begin
        SomeAsyncMethod checks whether the task completed. Let's suppose it did not.
        SomeAsyncMethod makes a delegate that writes "End" and assigns it as the continuation to the task (*)
        SomeAsyncMethod returns
    Caller invokes Something2, which returns
    Caller invokes Something3, which returns
    Suppose at this point the task completes. The IO system queues up a message. (**)
    Caller invokes Something4, which returns
    Caller returns
If there are messages queued up before the completion message, they are processed.
Eventually the message loop sees that there is a queued message saying that a task has completed. It invokes the completion.
    "End" is written.
    The completion returns.
The message loop keeps on pumping messages and doing work.

(*)这是一个过度紧张的谎言。制作的实际代表要复杂得多。

(**)这也有点过于简单化了,但它可以解决这个问题。

现在,读者会仔细询问&#34;如果该线程忙于调用Something3,该任务究竟如何将消息放入消息队列?当然{1}}正在运行时处理器只是简单地插入延续中,这一点都是不可能的,对吧?这实际上并不是魔法 - 尽管它比平凡的事件循环更加神奇! - 并且在Stephen Cleary的文章&#34; There Is No Thread&#34;中有解释,我建议你阅读。

答案 1 :(得分:1)

与使用yield非常不同,因为yield对线程没有任何作用。

我强烈建议观看史蒂夫桑德森的演讲(你将会看到大约33%的成绩,所以不需要观看整整一个小时)。他在一开始就有一个很好的类比,他在餐厅里谈到服务员:

你不想让你的服务员在你吃饭的时候站在你的桌旁 - 服务员最好补充一些人的水,接受命令等等。然后,当你吃完饭后,服务员回来给你检查

https://channel9.msdn.com/Events/TechDays/Techdays-2012-the-Netherlands/2287