基于我在MSDN
上阅读的内容async和await关键字不会导致额外的线程 创建。异步方法不需要多线程,因为异步 方法不会在自己的线程上运行。该方法在当前运行 同步上下文并仅在线程上使用时间 方法是有效的。您可以使用Task.Run将CPU绑定的工作移动到 后台线程,但后台线程对进程没有帮助 只是等待结果可用。
基本上说没有线程会被创建。但是在我继承的HttpClient
课程中,我发现了一些有趣的东西......
async Task<T> SendAsync<T>(HttpMethod method, Uri uri, HttpContent content, CancellationToken cancellationToken)
{
HttpRequestMessage msg = new HttpRequestMessage(method, uri);
msg.Content = content;
//On Main Thread
HttpResponseMessage response = await SendAsync(msg, cancellationToken);
//On worker thread
//...
}
在static void Main
Task result = client.GetAsync<string>(...);
//GetAsync calls await SendAsync<T>
result.Wait();
为什么我在await SendAsnc
电话后处于单独的线程?我认为asyn没有新的主题。或者至少在等待之后它将被调用回原始线程。
答案 0 :(得分:12)
这在文档中很少公布,但是由于控制台应用程序中缺少同步上下文,async / await在控制台应用程序中的工作方式与在UI应用程序中的工作方式非常不同。 This article描述了详细信息,并提供了如何添加一个代码示例,以便async / await以更可预测的方式运行。
通常,您对async / await绝对正确必然需要进行多线程处理(尽管像Task.Run 这样做,实际上会导致代码进入在线程池中运行的问题),但是(在我链接的文章中描述)在控制台应用程序中,async / await可以在任何地方运行。
我在通常在同一个线程上运行时如何工作的类比是想到去一家餐馆和另外9个人(总共10个人)。当服务员来的时候,有9个人准备好了,第10个人没有准备好。在这种情况下,服务员将首先接受其他9个人的订单,然后再回到第10个人。如果由于某种原因,第10个人真的慢了,服务员总是可以将订单带回厨房,并在第10个人准备好时回来。
显然,没有必要引进第二个服务员只是为了等待第10个人准备订购,而且让其他人等待一个人做好准备显然效率低下。为这种情况添加额外的服务员不会加快速度,因为延迟不是由于缺乏服务员造成的,而是由于第10个人慢慢下定决心造成的(并且&和等待工作人员无法做到这一点。答案 1 :(得分:9)
我在introduction to async
blog post中描述了这种行为。
总之,这是正确的:
async和await关键字不会导致创建其他线程。
这就是:
我认为asyn没有新的主题。
这不正确:
或者至少在等待之后它将被调用回原始线程。
async
和await
- 自己 - 不会导致创建任何其他线程。但是,在await
完成后,async
方法的其余部分必须在某处运行 。
默认情况下,await
会捕获“上下文”,即当前SynchronizationContext
(除非它是null
,在这种情况下,当前上下文是当前TaskScheduler
)。 UI线程有自己的同步上下文(例如,WinFormsSynchronizationContext
或DispatcherSynchronizationContext
),在这种情况下,async
方法将继续在同一个UI线程上执行。
在控制台应用程序中,没有同步上下文,因此await
捕获的“上下文”将是线程池上下文。因此,当async
方法准备好恢复时,它将被调度到线程池并由那里的某个线程拾取。
答案 2 :(得分:1)
控制台应用程序有一个线程池SynchronizationContext,而不是GUI和ASP.Net应用程序中使用的一次一个块的SynchronizationContext,所以当await完成时,它会调度async方法的剩余部分。线程池线程。 有关异步等待行为的更多信息,请阅读this article。