如果异步方法是单线程的,它如何在后台运行?

Posted

技术标签:

【中文标题】如果异步方法是单线程的,它如何在后台运行?【英文标题】:If an async method is single threaded how can it be run in the background? 【发布时间】:2015-05-29 10:33:21 【问题描述】:

我正在尝试理解 async/await 并阅读了许多文章,但仍然对同步/异步性质感到困惑。

我有以下测试控制台应用程序:

static void Main(string[] args)

    var test = FooAsync();
    Console.WriteLine("After FooAsync");

    for (int i = 0; i < 100; i++)
        Console.WriteLine("After that");

    Console.ReadKey();


private static async Task FooAsync()

    Console.WriteLine("Before delay");
    await Task.Delay(1);
    Console.WriteLine("After delay");

代码给出的输出如下:

Before delay
After FooAsync
After that
After that
After that
After that
After delay
After that
.
.

我知道 async/await 不会创建一个单独的线程进行处理,并且在FooAsync 到达await Task.Delay(1) 行时,它将返回到Main,因为任务将然而,还没有完成,因为我们只在一个线程上运行,有人可以解释是什么触发了FooAsync 方法在Main 内的某个任意点恢复,然后Main 可以继续吗?

更新 我收回它,i3arnon 和 dariogriffo 是正确的。该代码确实使用了多个线程(正如我之前在调试器中看到的那样,或者按照 kha 的建议做了明显的事情)。我对下一页https://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_Threads 上的线程部分感到困惑,没有意识到“继续”实际上是指“等待”任务完成后立即运行的继续任务计划。

【问题讨论】:

i3arnon 的回答是正确的,但如果您想完全理解它,请将您的 FooAsync 替换为:private static async Task FooAsync() Console.WriteLine("Before delay on " + Thread.CurrentThread.ManagedThreadId); await Task.Delay(1); Console.WriteLine("After delay on " + Thread.CurrentThread.ManagedThreadId); 并查看之前和之后在单独的线程上完成。 【参考方案1】:

这不是单线程

当延迟任务完成时,该方法的其余部分将发布到ThreadPool 并与您的主线程同时运行。这里的“触发器”是Task.Delay内部使用的内部System.Threading.Timer的回调。

此行为取决于SynchronizationContext。在 UI 环境中,这将被发布到同一个 UI 线程,并且必须等到该线程空闲。

如果您一直在等待从FooAsync 返回的任务那么您每次只有一个线程运行

【讨论】:

请参阅this answer,了解如何在Main 中等待FooAsync 返回的任务,以便FooAsync 的继续在主线程上运行。另外请注意,无需等待FooAsyncFooAsync 抛出的异常将被默默吞噬。【参考方案2】:

Async/await 可能会创建新线程,也可能不会,这取决于操作的性质。 如果操作是 IO(例如磁盘/网络操作),则可能以不会旋转新线程的方式进行编码。您可以从这里阅读:

The async and await keywords don't cause additional threads to be created?

如果你创建了自己的异步操作并创建了一个线程,那就是另一回事了,这就是为什么你不应该做异步而不是同步

http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx

您也可以检查这一点,但使用 Thread.CurrentThread 来获取进程的 Id。 (将其添加到 Console.WriteLine)

【讨论】:

【参考方案3】:

asyncawait 关键字创建新线程是一个很常见的误解。他们没有。

线程是通过运行Task 创建的。在这种情况下,线程由Task.Delay 调用创建。

【讨论】:

以上是关于如果异步方法是单线程的,它如何在后台运行?的主要内容,如果未能解决你的问题,请参考以下文章

后台任务永远运行?

在后台线程上执行异步套接字

c# - 如何强制关闭运行它自己的线程的后台工作者

如何在Android开发中用AsyncTask异步更新UI界面

在后台运行长时间运行的并行任务,同时允许小型异步任务更新前台

在后台线程中运行处理程序消息