Parallel.ForEach 循环 ThreadAbortException 已超过最大等待/睡眠/加入时间

问题描述 投票:0回答:1

我在 .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 否则它可以在清理之前保留许多打开的套接字。

c# multithreading dotnet-httpclient parallel.foreach .net-4.8
1个回答
2
投票

这是对

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)=>{
    ...
    });

如果文件很大,充斥网络,您可能需要指定一个较低的数字,或者如果有很多较小的文件,或者响应需要很长时间才能开始,您可以增加它。

© www.soinside.com 2019 - 2024. All rights reserved.