异步等待和线程

本文关键字:线程 等待 异步 | 更新日期: 2023-09-27 18:14:49

根据我在MSDN上读到的

async和await关键字不会导致额外的线程被占用创建。异步方法不需要多线程,因为Async方法不在其自己的线程上运行。该方法依靠电流运行的情况下才使用线程上的时间方法是活动的。您可以使用Task。运行将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调用后我在一个单独的线程上?我以为异步不会产生新的线程。或者至少它将在await之后被调用回原始线程。

异步等待和线程

在文档中没有很好地宣传这一点,但是由于控制台应用程序中缺乏同步上下文,因此async/await在控制台应用程序中的工作方式与在UI应用程序中的工作方式非常不同。本文描述了细节,并给出了一个代码示例,说明如何添加一个async/await以更可预测的方式运行。

通常情况下,你对async/await的看法是绝对正确的,而不是必须涉及多线程(尽管像Task。Run do实际上会导致相关代码在线程池中运行),但是(如我链接到的文章中所述)在控制台应用程序中async/await可以在任何地方运行。

对于异步运行在同一线程上时的工作方式,我通常的类比是想象与其他9个人一起去餐馆(总共10个人)。当服务员过来的时候,有9个人准备好了,而第10个人还没有。在这种情况下,服务员会先给另外9个人点餐,然后再回来给第10个人点餐。如果由于某种原因,第十个人的速度真的慢,服务员总是可以把点的菜带回厨房,等第十个人准备好了再回来。

显然,为了等待第10个人准备好点餐而再叫一个服务员是没有意义的,而且让其他人都等待第一个人准备好显然是低效的。增加额外的服务员并不会加快速度,因为延误不是因为没有服务员造成的,而是因为第十个人迟迟没有下定决心(服务员对此无能为力)。

我在介绍async的博文中描述了这种行为。

总之,这是正确的:

async和await关键字不会导致创建额外的线程。

我以为异步不会创建新线程。

这是不是正确:

或者至少它会在等待后被调用回原线程。

asyncawait - 本身 -不会导致创建任何额外的线程。然而,在await完成后,async方法的其余部分必须在某处运行

默认情况下,await捕获一个"上下文",这是当前SynchronizationContext(除非它是null,在这种情况下,当前上下文是当前TaskScheduler)。UI线程有自己的同步上下文(例如,WinFormsSynchronizationContextDispatcherSynchronizationContext),在这种情况下,async方法将继续在同一个UI线程上执行。

在控制台应用程序中,没有同步上下文,因此await捕获的"上下文"将是线程池上下文。因此,当async方法准备恢复时,它将被调度到线程池中,并由线程池中的某个线程拾取。

控制台应用程序有一个线程池SynchronizationContext,而不是GUI和ASP中使用的一次一个块的SynchronizationContext。因此,当await完成时,它将异步方法的剩余部分调度到线程池线程上。关于async await行为的更多信息,请阅读本文。