在转移到与调用者相同的线程之前,await是否在内部创建另一个线程(对于UI应用程序)

时间:2016-09-15 01:29:06

标签: c# multithreading asynchronous async-await

我对async / await的了解是,当任务完成时,继续在调用await时在相同的上下文中运行,在我的情况下,这将是UI线程。 但我的问题是,在IO完成之后和移动到同一个UI线程之前,它是否在内部创建了一个新线程。

我正在分享一段代码。如果我单击此按钮一次,它会在执行await之前显示可用线程为1023,但在此之后,可用线程下降到1022.虽然当我检查线程ID时,它与UI线程相同。

ipcRenderer

但有趣的是,下次单击此按钮时,可用线程数仍为1023(等待之前和之后)。

2 个答案:

答案 0 :(得分:5)

  

但我的问题是,在IO完成之后和移动到同一个UI线程之前,它是否在内部创建了一个新线程。

其他主题可能暂时使用,但您无需担心。

特别是,.NET上的I / O通常会通过一个I / O完成端口,该端口是线程池的一部分。根据需要自动添加和删除I / O线程。通常,I / O在实际准备返回到您的代码之前还有一些额外的工作要做(例如,解析HTTP响应头),因此BCL I / O代码的 lot 将会实际上只使用I / O线程将工作排队到线程池。因此,线程池工作线程通常由I / O代码(简要地)使用。

此外,在这个特定的例子中,我相信还有一个单独的计时器线程,它可以合并系统计时器。当然,这是一个实施细节,可能会有所变化。

因此,总而言之,可能会创建/销毁/临时使用其他线程,但您不必担心它。它们都以非常有效的方式由BCL或.NET运行时管理,在重用线程(最小化流失)和最小化资源使用(尤其是内存)之间取得平衡。

答案 1 :(得分:0)

我猜你的意思是它降到1022年。

一般来说,我认为这取决于正在进行的异步调用。磁盘和网络调用将从I / O完成线程池返回到一个线程。似乎Task.Delay在常规工作线程上返回。

您可以将行更改为await Task.Delay(5000).ConfigureAwait(false);,在其后设置断点并检查“线程”窗口以直接查看。

无论你从哪里调用它,都会在工作线程上完成。 await不会将调用上下文传递给实现异步操作的函数;它只会在完成后添加返回UI线程的额外步骤。

我不会过多地追踪这里的确切数字; CLR有自己的算法来管理线程池大小,并且这些算法可以在不同版本之间进行更改。我不会强调它在那里使用不同的线程:在一个普通的应用程序中,它将简单地重用池中的现有线程,操作将非常快。

相关问题