我想我终于在以下段落之后理解了它
如果使用或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 ( ) { TaskgetWebPageTask = 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(); TaskgetWebPageTask = GetWebPageAsync("http://stackoverflow.com"); Console.WriteLine("Begin"); Something2(); Something3(); string task = [ string returned by GetWebPageAsync("http://stackoverflow.com") ] Console.WriteLine("End"); Something4();
所以它很像一个yield
声明,因为执行在方法的调用者和方法体之间切换.
我现在明白了吗?
我现在明白了吗?
不,不完全.存储的任务的完成getWebPageTask
不会导致当前运行的线程Something3
丢弃正在执行的操作并运行其余部分SomeAsyncMethod
.(除非有一些真正奇怪的事情发生,比如其中一种方法在抽取消息循环.)
相反,延续计划在未来某个未指定的时间运行.
这是如何运作的?假设您使用的是Windows窗体应用程序,因为这很容易理解.你可能已经注意到在winforms应用程序中,一切都是"事件驱动的".也就是说,当事件发生时,事件处理程序会以某种方式神秘地执行.这不是魔术.顶部有一个循环,它将消息队列中的窗口消息拉出,解码它们,并确定是否有与之关联的事件处理程序.如果有则处理程序运行; 它只是一个普通的方法调用.
当任务在Windows窗体应用程序中完成时,它会在队列中放入一条消息,表示"刚刚发生了任务完成事件;当您处理此消息时,请调用此延续".
这就是为什么将来会在某个时间调用continuation的原因- 消息循环不会再次运行,直到它刚刚调用的所有代码都返回给它.如果任务在发生时完成,则消息将位于队列中以便按顺序处理.
现在,其他控制流程也是可能的.您可以配置一个线程,以便在该线程启动的任务完成时,计划在将来在任务管理器选择的工作线程上运行完成.但是,在我们跑步之前,让我们走吧; 在尝试弄清楚它在多个线程上是如何工作之前,请确保您了解它在一个线程上是如何工作的.
假设您的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.
(*)这是一个过度紧张的谎言.制作的实际代表要复杂得多.
(**)这也有点过于简单化了,但它得到了一个想法.
现在,读者会仔细询问"如果该线程忙于调用,该任务究竟如何将消息放入消息队列中Something3
?当然,这就像处理器在Something3
运行时简单地插入延续时一样是不可能的,对吧?这实际上并不是神奇的 - 虽然它比平凡的事件循环更加神奇! - 并且在Stephen Cleary的文章"没有线程"中有解释,我建议你阅读.