如果异步方法是单线程的,它如何在后台运行?
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
的继续在主线程上运行。另外请注意,无需等待FooAsync
,FooAsync
抛出的异常将被默默吞噬。【参考方案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】:async
或 await
关键字创建新线程是一个很常见的误解。他们没有。
线程是通过运行Task
创建的。在这种情况下,线程由Task.Delay
调用创建。
【讨论】:
以上是关于如果异步方法是单线程的,它如何在后台运行?的主要内容,如果未能解决你的问题,请参考以下文章
如何在Android开发中用AsyncTask异步更新UI界面