我在 .NET 4.8 应用程序中使用
Parallel.ForEach
循环从 Web API 并行下载多个文件。ThreadAbortException
/ 最长等待/睡眠/加入时间已超过 (00:05:00)。Parallel.ForEach
循环内踏板的“最大等待超时”。
导致问题的示例代码。
// create the httpclient
using (HttpClient httpClient = new HttpClient())
{
// now try to get documents in a parallel threaded task.
Parallel.ForEach(documentIds,
new ParallelOptions { MaxDegreeOfParallelism =
Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 0.50) * 2.0))},
(documentId) =>
{
try
{
// post the data to the service
using (HttpResponseMessage response =
httpClient.PostAsync(serviceURL, new StringContent(
requestBody.ToString())).GetAwaiter().GetResult())
{
// show error if service failed
if (response.IsSuccessStatusCode == false)
{
throw new Exception("Error getting content. " +
response?.ReasonPhrase);
}
// get back content
using (Stream responseStream = response.Content.ReadAsStreamAsync()
.GetAwaiter().GetResult())
{
// write the file to the FileSystem
}
}
}
catch (Exception ex)
{
// eat the error
// NOTE: This does catch the ThreadAbortException but it breaks out
// of the Parallel.ForEach BUT I want it to continue with other documents
}
});
}
更新一
我让它工作,但解决方案不是一个好的。
基本上是 HttpClient 的问题。
如果我在 Parallel.ForEach 循环中创建一个新的 HttpClient,那么它会让我在不跳出循环的情况下处理 ThreadAbortException。
MS 最佳实践说不要创建多个 HttpClients 否则它可以在清理之前保留许多打开的套接字。
这是对
Parallel.ForEach
的误用。该方法根本不适用于并发操作,only 意味着内存中数据并行。此代码在等待响应时阻止所有 CPU 内核自旋等待。这意味着在线程开始被逐出之前,它会将 CPU 保持在 100% 的状态一段时间。
使用
Parallel.ForEachAsync
代替,或Dataflow ActionBlock,例如:
await Parallel.ForEachAsync(documentIds, async (documentId,ct)=>{
...
});
这将使用与处理数据的核心数量大致相同的工作任务。您可以使用
ParallelOptions
参数指定不同的数字(更高或更低)
await Parallel.ForEachAsync(documentIds,
new ParallelOptions { MaxDegreeOfParallelism = 4},
async (documentId,ct)=>{
...
});
如果文件很大,充斥网络,您可能需要指定一个较低的数字,或者如果有很多较小的文件,或者响应需要很长时间才能开始,您可以增加它。