我想我终于在以下段落之后理解了它
如果使用或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
语句,因为执行在方法的调用者和方法体之间切换。
我现在能理解吗?
答案 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