是.net任务线程在等待异步操作完成时临时返回池的资源

本文关键字:返回 资源 完成时 任务 net 线程 在等待 异步操作 | 更新日期: 2023-09-27 18:18:02

我有一个TPL任务,它做两件事。首先,它调用web服务。其次,它将一些数据插入数据库。我有多达20个任务一次开始做同样的事情一遍又一遍。他们整天所做的就是调用web服务并将数据插入数据库。

我对。net中的TPL相当陌生。我已经做了一些后台工作进程和异步web服务的东西。

web服务调用和数据库插入都阻塞了Task所在线程内的调用。

我知道在幕后,当你使用任务时,. net为你管理一个线程池。是吗?

如果我使用async和await()进行服务调用和数据库调用,而不是使它们阻塞调用,线程池会有更多的线程可供使用吗?

我的理论(我不确定为什么我认为这一点)是线程在等待阻塞web服务时忙着什么也不做,并且不能暂时将其资源返回到池中。但是我想知道任务是否在等待异步调用完成,主任务线程是否能够切换到让其他东西在等待时处理。

我的理论对吗?还是我在胡编乱造?

我正在使用c#和。net 4.0,但如果需要,我可以使用4.5。

是.net任务线程在等待异步操作完成时临时返回池的资源

线程池是否有更多的线程可供使用服务调用和数据库调用使用async和await()代替给他们打骚扰电话?

这取决于你所说的"利用async-await"是什么意思。

当您使用Task.Run时,在幕后,Task类使用ThreadPool来使用ThreadPool线程卸载工作。

如果你的服务没有公开一个真正的异步api,你使用Task.Run来排队你的工作,你仍然会阻塞一个线程池线程来做IO绑定的工作,不管使用async-await。在你的问题中,你说两个调用都是阻塞调用,在这种情况下,答案是否定的,用于进行这些阻塞调用的线程池线程仍然会被阻塞。

如果你的服务和数据库调用是真正的异步api(不消耗任何额外的线程来完成它的工作),你可以利用async-await,当你在其中一个调用上await时(你不需要使用Task)。使用它们运行),当前线程将把控制权交还给调用者,并可在此期间用于完成更多工作。如果是这样,那么是。

我的理论(我不确定为什么我认为这一点)是线程在等待阻塞web服务时忙着什么也不做,并且不能暂时将其资源返回到池中。但是我想知道任务是否在等待异步调用完成,主任务线程是否能够切换到让其他东西在等待时处理。

你的理论是正确的。如果排队线程池的主要工作是发出IO绑定请求,那么它的大部分时间都是阻塞,直到请求完成。

当你await a Task时,控制权返回给调用者。让我们假设你的服务调用是一个REST调用,你可以使用HttpClient,它暴露了真正的非线程消耗异步方法,如GetAsync, PostAsync,当你await这些调用时,你的调用线程被释放,同时做更多的工作。

如果应用程序的所有任务都阻塞,每个任务将使用线程池中的一个线程。

如果所有任务定期await,则线程池不需要为每个任务使用一个线程。

当你的代码await是一个尚未完成的操作时,该方法的状态被保存,以便它可以在任何其他线程上恢复。

空闲的线程池线程会在一段时间后被释放,所以当调用await的方法仍在运行时,遇到await的实际线程可以从线程池中被释放。

把所有这些放在一起,一个异步版本的例程可以用更少的线程完成同样的工作(假设工作负载在等待时间和旋转CPU之间有足够的平衡)。

这段代码运行100个同步等待的任务:

var numTasks = 100;
for (int i = 0; i < numTasks; i++)
{
    Thread.Sleep(5);
    Task.Run(() =>
    {
        Thread.Sleep(5000);
        Interlocked.Decrement(ref numTasks);
    });
}
while (numTasks > 0) Thread.Sleep(100);

对于异步等待,更改为:

    Task.Run(async () =>
    {
        await Task.Delay(5000);
        Interlocked.Decrement(ref numTasks);
    });

在我的系统中,异步版本的峰值线程数增加了一半,并且需要20%的时间来完成相同的"工作"。

答案是肯定的。虽然从技术上讲,它不是"等待"异步操作完成(否则async就没有好处了)。在底层有一个回调委托,它在异步操作完成时运行,这允许调用线程在不阻塞的情况下继续。是async/await的魔力把这些"延续"变成了线性的代码。

因为你正在使用一个线程池线程,当它遇到await时,线程将返回到线程池。这里需要注意的是,正常的行为是当等待的操作完成时,它将尝试回到它启动的线程上(现在可能正在被另一个任务使用),因此您可能会观察到获得结果的延迟问题,因为线程池线程现在正在启动其他任务。随着时间的推移,线程池将尝试调整可用线程的数量以满足需求,但如果您的工作是突发的,您可能会发现这不会很快发生。结果将是明显的性能差,因为您可能只有少量的线程可用。