我有一个巨大的网页列表,显示状态,我需要检查。 一些网址位于同一网站内,另一个网址位于另一个网站上。
现在我正试图通过使用下面的代码以并行的方式做到这一点,但我感觉我造成了太多的开销。
while(ListOfUrls.Count > 0){
Parallel.ForEach(ListOfUrls, url =>
{
WebClient webClient = new WebClient();
webClient.DownloadString(url);
... run my checks here..
});
ListOfUrls = GetNewUrls.....
}
这可以用更少的开销完成,并且可以更多地控制我使用/重用的webclients和连接数量吗?那么,最终工作可以更快完成吗?
答案 0 :(得分:6)
Parallel.ForEach
适用于受CPU约束的计算任务,但在您的情况下,它将不必为像DownloadString
这样的同步IO绑定调用阻塞池线程。您可以使用DownloadStringTaskAsync
和任务来提高代码的可伸缩性并减少可能使用的线程数:
// non-blocking async method
async Task<string> ProcessUrlAsync(string url)
{
using (var webClient = new WebClient())
{
string data = await webClient.DownloadStringTaskAsync(new Uri(url));
// run checks here..
return data;
}
}
// ...
if (ListOfUrls.Count > 0) {
var tasks = new List<Task>();
foreach (var url in ListOfUrls)
{
tasks.Add(ProcessUrlAsync(url));
}
Task.WaitAll(tasks.ToArray()); // blocking wait
// could use await here and make this method async:
// await Task.WhenAll(tasks.ToArray());
}
答案 1 :(得分:2)
你可以尝试在.Net 4.5中使用HttpClient一个新增功能它认为速度更快,它可能会提高你的性能
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(url))
using (HttpContent content = response.Content)
{
string result = await content.ReadAsStringAsync();
}
答案 2 :(得分:0)
应用程序的web.config或app.config文件中经常被忽略的元素是connectionManagement标记。特别是,默认情况下,.NET会将同时连接到域的连接数限制为2。您可以看到标记here的文档。
如果我正确地理解了您的问题,那么默认情况下并行创建2个域的Web客户端将被限制为4个线程(每个域2个线程),导致加速比您预期的要少。
但是,如果要连接到多个域,那么其他答案可能会产生更多的加速,因为等待响应可能是每次循环迭代成本的很大一部分。如果您使用的是.NET 4.5,GetStringAsync
方法可能是您的朋友。
答案 3 :(得分:-1)
您是否考虑过代码的异步执行?我认为没有更快的方式从互联网上获取数据,但你可以同时进行。