【发布时间】:2015-11-25 23:19:33
【问题描述】:
我有一个项目枚举 (RunData.Demand),每个项目代表一些涉及通过 HTTP 调用 API 的工作。如果我只是 foreach 完成所有操作并在每次迭代期间调用 API,效果会很好。但是,每次迭代需要一两秒钟,所以我想运行 2-3 个线程并在它们之间分配工作。这就是我正在做的事情:
ThreadPool.SetMaxThreads(2, 5); // Trying to limit the amount of threads
var tasks = RunData.Demand
.Select(service => Task.Run(async delegate
{
var availabilityResponse = await client.QueryAvailability(service);
// Do some other stuff, not really important
}));
await Task.WhenAll(tasks);
client.QueryAvailability 调用基本上是使用 HttpClient 类调用 API:
public async Task<QueryAvailabilityResponse> QueryAvailability(QueryAvailabilityMultidayRequest request)
{
var response = await client.PostAsJsonAsync("api/queryavailabilitymultiday", request);
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsAsync<QueryAvailabilityResponse>();
}
throw new HttpException((int) response.StatusCode, response.ReasonPhrase);
}
这在一段时间内效果很好,但最终事情开始超时。如果我将 HttpClient Timeout 设置为一小时,我就会开始收到奇怪的内部服务器错误。
我开始做的是在QueryAvailability 方法中设置一个秒表来查看发生了什么。
正在发生的情况是 RunData.Demand 中的所有 1200 个项目同时被创建,并且所有 1200 个await client.PostAsJsonAsync 方法都被调用。然后它似乎使用 2 个线程来缓慢地检查任务,所以到最后我的任务已经等待了 9 或 10 分钟。
这是我想要的行为:
我想创建 1,200 个任务,然后在线程可用时一次运行 3-4 个。我确实不想立即排队 1,200 个 HTTP 调用。
有什么好的方法可以做到这一点吗?
【问题讨论】:
-
您似乎没有为每次通话创建一个新的
client。您知道System.Net.Http.HttpClient对于实例调用不是线程安全的吗?应该为每次调用创建(并在之后处理)一个新实例。 -
QueryAvailability方法实际上位于创建HttpClient的类中,HttpClient是该实例的私有成员。虽然我不知道它不是线程安全的,但我绝对可以在每次调用之前创建它。我会进一步研究,谢谢!
标签: c# .net multithreading asynchronous async-await