从等待返回后出现意外行为

Let*_*man 5 c# asynchronous async-await

我知道有很多关于async/await的问题,但我找不到任何答案.

我遇到了一些我不理解的东西,请考虑以下代码:

void Main()
{
    Poetry();
    while (true)
    {
        Console.WriteLine("Outside, within Main.");
        Thread.Sleep(200);
    }
}

async void Poetry()
{
   //.. stuff happens before await
   await Task.Delay(10);
   for (int i = 0; i < 10; i++)
   {
       Console.WriteLine("Inside, after await.");
       Thread.Sleep(200);
   }
}
Run Code Online (Sandbox Code Playgroud)

显然,在await运算符上,控件返回给调用者,而正在等待的方法正在后台运行.(假设IO操作)

但是在控制返回到await运算符之后,执行变为并行,而不是(我的期望)保持单线程.

我希望在"延迟"完成之后,线程将被强制回到Poetry方法,从它离开的地方继续.

它做的.对我来说奇怪的是,为什么"Main"方法一直在运行?是一个线程从一个跳到另一个?还是有两个平行的线程?

这不是一个线程安全问题吗?

我发现这令人困惑.我不是专家.谢谢.

Ste*_*ary 8

在博客上有关于async方法如何恢复的说明await.本质上,await捕获电流SynchronizationContext除非它null在哪种情况下捕获电流TaskScheduler.然后使用该"上下文"来安排该方法的其余部分.

由于您正在执行控制台应用程序,因此没有SynchronizationContext,并且TaskScheduler捕获默认值以执行该async方法的其余部分.该上下文将该async方法排队到线程池.除非你实际上给它一个带有SynchronizationContext(或TaskScheduler)队列到主循环的主循环,否则无法返回到Console应用程序的主线程.


Let*_*man 0

我想在这里回答我自己的问题。

你们中的一些人给了我很好的答案,这些都帮助我更好地理解(并得到了点赞)。可能没有人给我完整的答案,因为我没有提出完整的问题。无论如何,有人会遇到我的确切误解,我希望这是第一个答案(但我建议查看下面的更多答案)。

因此,Task.Delay 使用Timer操作系统在 N 毫秒后触发事件。在此期间之后,将创建一个新的池线程,它基本上几乎不执行任何操作。

wait 关键字意味着线程完成后(并且它几乎不执行任何操作),它应该继续执行 wait 关键字之后的任何操作。

正如其他答案中提到的,这是同步上下文。

如果没有这样的上下文,则相同的新创建的池线程将继续运行等待之后发生的事情。

如果存在同步上下文,则新创建的池线程只会将等待之后的任何内容推送到同步上下文中。

为了这个目的,这里有几点我没有意识到的:

  • async/await 没有做任何以前(从技术上来说)不可能的事情。只是也许非常笨拙。
  • 它只是对某些 .NET 4.5 类的语言支持。
  • 这很像收益率。它可能会将您的方法分解为几个方法,甚至可能在后面生成一个类,并使用 BCL 中的一些方法,但仅此而已。

不管怎样,我建议阅读C# 5.0 In A Nutshell的“并发与异步”一章。这对我帮助很大。太棒了,实际上解释了整个故事。