我需要从大约3000个URL中获取内容。我使用的是 HttpClient
创作 Task
对于每个url,添加任务到列表,然后 await Task.WhenAll
. 类似这样
var tasks = new List<Task<string>>();
foreach (var url in urls) {
var task = Task.Run(() => httpClient.GetStringAsync(url));
tasks.Add(task);
}
var t = Task.WhenAll(tasks);
然而很多任务最终都是在 Faulted
或 Canceled
州。我想可能是具体的URL的问题,但不是。我可以用curl并行获取这些url没有问题。
我试过 HttpClientHandler
, WinHttpHandler
与各种超时等。总是有几百个urls以错误告终.然后我试着以10个为一批来获取这些urls,这样就可以了。没有错误,但非常慢。Curl会以并行的方式快速获取3000个urls.然后我试着以10个为单位来获取这些urls,没有错误,但非常慢。httpbin.org 3000次,以验证问题是否与我特定的urls无关。
var handler = new HttpClientHandler() { MaxConnectionsPerServer = 5000 };
var httpClient = new HttpClient(handler);
var tasks = new List<Task<HttpResponseMessage>>();
foreach (var _ in Enumerable.Range(1, 3000)) {
var task = Task.Run(() => httpClient.GetAsync("http://httpbin.org"));
tasks.Add(task);
}
var t = Task.WhenAll(tasks);
try { await t.ConfigureAwait(false); } catch { }
int ok = 0, faulted = 0, cancelled = 0;
foreach (var task in tasks) {
switch (task.Status) {
case TaskStatus.RanToCompletion: ok++; break;
case TaskStatus.Faulted: faulted++; break;
case TaskStatus.Canceled: cancelled++; break;
}
}
Console.WriteLine($"RanToCompletion: {ok} Faulted: {faulted} Canceled: {cancelled}");
同样,总是有几百个任务以错误结束。
那么,这里的问题是什么?为什么我不能用 async
?
我使用的是.NET Core,因此建议使用ServicePointManager(试图并行运行多个HTTP请求,但受到Windows的限制(注册表)。)是不适用的。
另外,我需要获取的urls指向不同的主机。httpbin的代码只是一个测试,以表明问题不在于我的urls是无效的。
正如菲尔德在评论中所说。httpClient.GetStringAsync
返回 Task
. 所以,你不需要把它包起来 Task.Run
.
我在控制台应用中运行了这段代码。花了50秒才完成。在你的评论中,你写道,curl在不到一分钟的时间内执行3000次查询--同样的事情。
var httpClient = new HttpClient();
var tasks = new List<Task<string>>();
var sw = Stopwatch.StartNew();
for (int i = 0; i < 3000; i++)
{
var task = httpClient.GetStringAsync("http://httpbin.org");
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
sw.Stop();
Console.WriteLine(sw.Elapsed);
Console.WriteLine(tasks.All(t => t.IsCompleted));
此外,所有的请求都成功完成。
在你的代码中,你在等待任务开始时使用了 Task.Run
. 但是,你需要等待完成通过调用 httpClient.Get...