运行时如何知道何时使用"等待"?

时间:2014-04-02 07:14:23

标签: c# async-await

修改

我接受了Jon的评论并重新审视了整个事情。事实上, 阻止了UI线程。我必须以某种方式弄乱我的初步测试。字符串“OnResume exits”在 SomeAsync完成后写为。如果方法更改为使用await Task.WhenAll(t),它将(按预期)不会阻止。感谢您的投入! 我首先考虑删除这个问题,因为最初的假设是错误的,但我认为答案包含了不应丢失的有价值的信息。

原帖:

试图了解async-await更深入的内部结构。以下示例来自使用Xamarin的Android应用。 OnResume()在UI线程上执行。

  • SomeAsync()开始一项新任务(=它产生一个线程)。然后它使用Task.WaitAll()执行阻塞等待(如果WhenAll()是更好的选择,我们现在不讨论。)
  • 我可以看到在Task.WaitAll()运行时UI未被阻止。所以SomeAsync()不在UI线程上运行。这意味着创建了一个新线程。

await如何“知道”它必须在这里产生一个线程 - 它会一直这样做吗?如果我将WaitAll()更改为WhenAll(),则根据我的理解,不需要额外的线程。

// This runs on the UI thread.
async override OnResume()
{
  // What happens here? Not necessarily a new thread I suppose. But what else?
  Console.WriteLine ("OnResume is about to call an async method.");
  await SomeAsync();

  // Here we are back on the current sync context, which is the UI thread.
  SomethingElse();
  Console.WriteLine ("OnResume exits");
}

Task<int> SomeAsync()
{
var t = Task.Factory.StartNew (() => {
    Console.WriteLine("Working really hard!");
    Thread.Sleep(10000);
    Console.WriteLine("Done working.");
});
Task.WhenAll (t);

return Task.FromResult (42);
}

2 个答案:

答案 0 :(得分:6)

简单:从不await生成一个主题。如果等待已经完成,它就会继续运行;如果等待已完成,它只是告诉等待实例添加 continuation (通过一个相当复杂的状态机)。当正在完成的事情完成时,将调用continuation(通常通过sync-context,如果有的话 - 在标记工作完成的线程上同步)。然而!理论上,sync-context可以选择将事物推送到线程池(大多数UI同步上下文,但是,将内容推送到UI线程)。

答案 1 :(得分:1)

我想你会发现这个帖子很有趣:How does C# 5.0's async-await feature differ from the TPL?

简而言之,await不会启动任何线程。

它做了什么,只是将代码“拆分”到了“await”放置的位置,以及将该行添加为 continuation 的所有内容。的任务

注意任务。请注意,您已获得Factory.StartNew。因此,在您的代码中,工厂实际上启动任务 - 它包括将其放在某个线程上,无论是UI还是池还是任何其他任务调度程序。这意味着,当您执行等待时,“任务”通常已经分配到某个调度程序。

当然,它不必分配,也不必开始。唯一重要的是你需要有一个任务,真的。

如果未启动任务 - await不关心。它只是附加延续,由您决定以后启动任务。并将其分配给适当的调度程序。