关于系统资源消耗和效率的Async/Await

本文关键字:Async Await 效率 系统资源 | 更新日期: 2023-09-27 18:13:14

简短版本:当异步方法在循环中被调用成千上万次时,这些方法可能调用其他异步方法,那么异步调用如何缩放?我的线程池会爆炸吗?

我一直在阅读和实验TPL和Async,在阅读了很多材料之后,我仍然对一些方面感到困惑,我找不到太多的信息,比如 Async调用如何缩放。我尽量开门见山。

异步调用
对于IO,我认为使用async比使用新线程/启动任务更好,但据我所知,在不使用不同线程的情况下执行异步操作是不可能的,这意味着async 必须在某些时候使用其他线程/启动任务。所以我的问题是:关于系统资源,代码A如何优于代码B ?

代码


// an array with 5000 urls.
var urls = new string[5000];
// list of awaitable tasks.
var tasks = new List<Task<string>>(5000);
HttpClient httpClient;
foreach (string url in urls)
{
    tasks.Add(httpClient.GetStringAsync(url));
}
await Task.WhenAll(tasks);

代码B

...same variables as code A...
foreach (string url in urls)
{
    tasks.Add(
              Task.Factory.StartNew(() =>
              {
                // This method represents a
                // synchronous version of the GetStringAsync.
                httpClient.GetString(url);
              })
             );
}
await Task.WhenAll(tasks);

这让我想到了以下问题:
1 -应该在循环中避免异步调用吗?
2 -是否有一个合理的最大的异步调用应该被触发,或者是触发任何数量的异步调用ok?这是如何衡量的呢?
3 -异步方法,在底层,为每个调用启动一个任务?

我用1000个url进行了测试,使用的线程池工作线程的数量从未达到30,IO完成线程的数量总是在5左右。

我的实践实验

我创建了一个带有简单异步控制器的web应用程序。该页面由一个带有文本区的单一表单组成,用户可以在其中输入他希望请求/执行某些操作的所有url。

在提交时,使用HttpClient在循环中请求url。GetUrlAsync方法就像上面的代码A

有趣的是,如果我提交1000个url,大约需要3分钟才能完成所有请求。

另一方面,如果我提交3形式从不同的标签(即客户),与1000年每个url,它需要很多时间结果(约10分钟),这真的让我很困惑,因为每msdn的定义,它不应该花很多时间超过3分钟,特别当即使在同一时间处理所有的请求使用线程的数量从threadpool大约25岁,这意味着资源不是很好了!

它现在的工作方式,这种类型的应用程序是远不可扩展的(假设我有大约5000个客户端请求一堆url),我不知道如何异步方式来触发多个IO请求。

关于应用程序的进一步说明

客户端:
1. 用户进入站点
2. 在文本区输入1000个url
3.提交url

服务器端:
1. 接收url作为数组
2. 执行代码

foreach (string url in urls)
{
    tasks.Add(GetUrlAsync(url));
}
await Task.WhenAll(tasks);
//at this point the thread is
// returned to the pool to receive
// further requests.
  • 通知客户端工作完成
  • 请告诉我吧!谢谢你。

    关于系统资源消耗和效率的Async/Await

    根据我的理解,执行异步操作而不使用不同的线程是不可能的,这意味着异步必须在某个时候使用其他线程/启动任务。

    不。正如我在博客中所描述的,纯异步方法不会阻塞线程。

    所以我的问题是:关于系统资源,代码A如何比代码B更好?

    A使用的线程比b少

    (附带说明,不要使用StartNew。它非常过时,并且具有非常危险的默认参数值。使用Task.Run代替。如果你从博客文章或文章中得到这个想法/代码,请把这个词传递下去。StartNew是一个似乎正在接管互联网的毒瘤。

    应该在循环中避免异步调用吗?

    不用了。

    是否有一个合理的最大异步调用应该被触发,或者是触发任何数量的异步调用ok?

    任何数量都可以,只要你的后端资源可以处理它。

    这个比例是多少?

    . net上的异步I/O几乎总是在底层使用iocp (I/O完成端口),这通常被认为是Windows上可用的最可扩展的I/O形式。

    异步方法,在底层,为每个调用启动一个任务吗?

    是和不是。每个异步方法的执行Task实例表示,但这些不代表运行任务-它们不代表线程。

    我将异步任务称为Promise任务,而不是Delegate任务(实际上在线程池上运行的任务)。

    真把我搞糊涂了

    测试URL请求时需要注意的一件事是,. net内置了对URL请求的自动限制。尝试设置ServicePointManager.DefaultConnectionLimitint.MaxValue