TPL 与异步/等待(线程处理)之间的区别

当涉及到线程创建时,试图理解 TPL 和 async/await之间的区别。

我相信 TPL (TaskFactory.StartNew)的工作方式类似于 ThreadPool.QueueUserWorkItem,因为它将线程池中的线程上的工作排队。当然,除非您使用创建新线程的 TaskCreationOptions.LongRunning

我认为 async/await的工作原理基本相同:

TPL:

Factory.StartNew( () => DoSomeAsyncWork() )
.ContinueWith(
(antecedent) => {
DoSomeWorkAfter();
},TaskScheduler.FromCurrentSynchronizationContext());

Async/Await:

await DoSomeAsyncWork();
DoSomeWorkAfter();

都是一样的。从我一直阅读的内容来看,似乎 async/await只是“有时”创建一个新的线程。那么它什么时候创建一个新线程,什么时候不创建一个新线程呢?如果你正在处理 IO 完成端口,我可以看到它不必创建一个新的线程,否则我会认为它将不得不。我想我对 FromCurrentSynchronizationContext的理解也总是有点模糊。我一直认为它本质上是 UI 线程。

29918 次浏览

Sync/wait 基本上简化了 ContinueWith方法(继续传球中的 Continuations)

它没有引入并发性——您仍然需要自己来做(或者使用框架方法的 Async 版本)

因此,C # 5版本应该是:

await Task.Run( () => DoSomeAsyncWork() );
DoSomeWorkAfter();

我相信 TPL (TaskFactory)。Startnew)类似于 ThreadPool。QueueUserWorkItem,因为它将线程池中的线程上的工作排队。

差不多。

从我一直在阅读它似乎异步/等待只有“有时”创建一个新的线程。

事实上,从来没有过。如果需要多线程,则必须自己实现它。有一个新的 Task.Run方法,它只是 Task.Factory.StartNew的简写,它可能是在线程池上启动任务的最常用方法。

如果你正在处理 IO 完成端口,我可以看到它不必创建一个新的线程,否则我会认为它将不得不。

所以像 Stream.ReadAsync这样的方法实际上会在 IOCP 周围创建一个 Task包装器(如果 Stream有 IOCP 的话)。

您还可以创建一些非 I/O、非 CPU 的“任务”。一个简单的例子是 Task.Delay,它返回在一段时间后完成的任务。

关于 async/await最酷的事情是你可以将一些工作排队到线程池(例如,Task.Run) ,做一些 I/O 绑定操作(例如,Stream.ReadAsync) ,以及做一些其他操作(例如,Task.Delay) ... 它们都是任务!他们可以等待或使用的组合,如 Task.WhenAll

任何返回 Task的方法都可以是 awaited-它不一定是 async方法。因此,Task.Delay和 I/O 绑定操作只是使用 TaskCompletionSource来创建和完成一个任务——线程池中唯一要做的事情是当事件发生时实际的任务完成(超时、 I/O 完成等)。

我想我对 FromCurrentSynchronizationContext 的理解也总是有点模糊。我一直认为它本质上是 UI 线程。

我在 SynchronizationContext上写 一篇文章。大多数时候,SynchronizationContext.Current:

  • 如果当前线程是 UI 线程,则为 UI 上下文。
  • 如果当前线程正在服务 ASP.NET 请求,则为 ASP.NET 请求上下文。
  • 否则就是线程池上下文。

任何线程 可以都设置自己的 SynchronizationContext,因此上述规则也有例外。

请注意,默认的 Task等待者将在当前 SynchronizationContext 如果它不为空上安排 async方法的其余部分; 否则它将在当前 TaskScheduler上进行。这在今天并不重要,但在不久的将来,这将是一个重要的区别。

我在我的博客上写了我自己的 ABC0/await简介,并且 Stephen Toub 最近发布了一个优秀的 ABC0/await常见问题

关于“并发”与“多线程”,请参阅 这个相关的 SO 问题。我认为 async支持并发,并发可能是多线程的,也可能不是。使用 await Task.WhenAllawait Task.WhenAny进行并发处理很容易,除非显式地使用线程池(例如,Task.RunConfigureAwait(false)) ,否则可以同时进行多个并发操作(例如,多个 I/O 或其他类型的 Delay)——而且这些操作不需要线程。对于这种情况,我使用术语“单线程并发”,但是在 ASP.NET 主机中,实际上可能会出现“ 线程并发”。这很贴心。