简短版本:当异步方法在循环中被调用数千次和数千次时异步调用如何扩展,这些方法可能会调用其他异步方法?我的线程池会爆炸吗?
我一直在阅读和试验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.
拜托,赐教! 谢谢。
答案 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
。