关于系统资源消耗和效率的异步/等待

时间:2015-09-16 22:16:24

标签: c# asynchronous

简短版本:当异步方法在循环中被调用数千次和数千次时异步调用如何扩展,这些方法可能会调用其他异步方法?我的线程池会爆炸吗?

我一直在阅读和试验TPL和Async,在阅读了很多材料后,我仍然对某些方面感到困惑,我找不到很多相关信息,比如 async调用。我会尽力直截了当。

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

代码A

// 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 - 是否有合理的最大异步调用一次应该被触发,或者是否正在触发任意数量的异步调用?这如何扩展?
3 - 引入异步方法是否为每次通话启动任务?

我用1000个网址测试了这个,并且使用过的线程池工作线程的数量甚至没有达到30,IO完成线程的数量总是大约为5个。

我的实践实验

我使用简单的异步控制器创建了一个Web应用程序。 该页面由一个带有textarea的表单组成,用户可以在其中输入他希望请求/做一些工作的所有URL。

在提交时,使用HttpClient.GetUrlAsync方法循环请求网址,就像上面的代码A 一样。

一个有趣的观点是,如果我提交1000个网址,则需要大约3分钟才能完成所有请求。

另一方面,如果我从3个不同的标签(即客户)提交3个表单,每个表单有1000个网址,结果需要更长的时间(大约10分钟),这真让我感到困惑,因为按照msdn定义,它不应该花费超过3分钟,特别是当同时处理所有请求同时从线程池中使用的线程数约为25时,这意味着资源根本没有被很好地探索!

现在的工作方式,这种类型的应用程序远非可扩展(假设我有大约5000个客户端一直请求一堆URL),而且我没有看到asyncis如何触发多个IO请求。

有关申请的进一步说明

客户方:
1.用户进入网站
2.在文本区域中键入1000个URL 3.提交网址

服务器端:
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.
  1. 通知客户工作已完成
  2. 拜托,赐教! 谢谢。

1 个答案:

答案 0 :(得分:5)

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

Nope。正如我在博客中描述的那样,纯异步方法不会阻止线程。

  

所以我的问题是:代码A如何比代码B更好地处理系统资源?

A使用的线程少于B。

(另请注意,请勿使用StartNew。它可能已过时并且具有非常危险的默认参数值。请改用Task.Run。如果您从此获得此想法/代码博客文章或文章,请传递这个词。StartNew是一种似乎正在接管互联网的癌症。)

  

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

不,没关系。

  

是否有合理的最大异步调用一次应该被触发,或者是否正在触发任意数量的异步调用?

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

  

这是如何扩展的?

.NET上的异步I / O几乎总是使用下面的IOCP(I / O完成端口),这通常被认为是Windows上可扩展的I / O形式。

  

根据需要,异步方法是否为每次调用启动任务?

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

我调用异步任务Promise Tasks, as opposed to Delegate Tasks(实际上在线程池上运行的任务)。

  

真让我困惑

在测试URL请求时要注意的一件事是,对.NET内置的URL请求进行自动限制。尝试将ServicePointManager.DefaultConnectionLimit设置为int.MaxValue