【发布时间】:2017-03-08 02:45:14
【问题描述】:
我有(我的 url 列表大约有 1000 个 url),我想知道是否有更有效的调用来自同一站点的多个 url(已经更改 ServicePointManager.DefaultConnectionLimit)。
另外,重复使用相同的HttpClient 还是在每次调用时创建一个新的更好,下面只使用一个而不是多个。
using (var client = new HttpClient { Timeout = new TimeSpan(0, 5, 0) })
{
var tasks = urls.Select(async url =>
{
await client.GetStringAsync(url).ContinueWith(response =>
{
var resultHtml = response.Result;
//process the html
});
}).ToList();
Task.WaitAll(tasks.ToArray());
}
正如@cory 建议的那样
这是使用TPL 的修改代码,但是我必须设置MaxDegreeOfParallelism = 100 以达到与基于任务的速度大致相同的速度,下面的代码可以改进吗?
var downloader = new ActionBlock<string>(async url =>
{
var client = new WebClient();
var resultHtml = await client.DownloadStringTaskAsync(new Uri(url));
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 100 });
foreach(var url in urls)
{
downloader.Post(url);
}
downloader.Complete();
downloader.Completion.Wait();
最终
public void DownloadUrlContents(List<string> urls)
{
var watch = Stopwatch.StartNew();
var httpClient = new HttpClient();
var downloader = new ActionBlock<string>(async url =>
{
var data = await httpClient.GetStringAsync(url);
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 100 });
Parallel.ForEach(urls, (url) =>
{
downloader.SendAsync(url);
});
downloader.Complete();
downloader.Completion.Wait();
Console.WriteLine($"{MethodBase.GetCurrentMethod().Name} {watch.Elapsed}");
}
【问题讨论】:
-
我建议使用 TPL 数据流来限制运行中的任务数量。在当前实现中,您会发现一件事是
HttpClient请求实际上可能会超时,即使它们尚未在网络上发送。 -
如果您的 CPU 上没有很多内核,那么大量的并行性是无用的,您只会遇到线程饥饿。尝试使用
await SendAsync而不是Post来释放线程,并且不要阻塞任务,一路使用await。 HttpClient 旨在从不同的线程中使用,所以不要每次都创建一个新的 -
@VMAtm 我的机器上有 6 个内核,我有点困惑,你能告诉我这段代码的样子吗?看来我要回到任务作为我的第一个代码没有?
-
@VMAtm 添加了最终代码,我想我全部捕获了
-
1.
Parallel.ForEach不支持异步。 2.您需要等待SendAsync。
标签: c# multithreading task-parallel-library tpl-dataflow