.Net Core 3.1中Parallel.ForEach使HTTPWebRequests / WebClient损坏

问题描述

我写了一个小程序,可以并行发出2000多个请求。

场景:

  1. DotNet Core 3.1:它在900个请求后完全冻结,并开始引发System.Net.WebException。
  2. DotNet Framework 4.7.2:运行得很好。
  • 我还尝试列出任务列表并将每个任务添加Parallel.ForEach中,然后在Task.WhenAll(tasks)之后等待所有任务完成(Parallel.ForEach),结果相同。
  • 我还尝试使用HttpWebRequest发出请求,并读取响应而不是使用WebClient,结果也相同。

这是DotNet Core 3.1的问题,还是我做错了什么?

这是我在两种情况下都运行的代码

static void Main(string[] args)
{
    if (!Directory.Exists(@"C:\temp\parallel_requests"))
        Directory.CreateDirectory(@"C:\temp\parallel_requests");

    // List with the request urls (simulated,in real life these urls will be the ones that we need)
    var urls = new List<string>();
    for (var i = 0; i < 2000; i++)
    {
        urls.Add(@"https://jsonplaceholder.typicode.com/todos/1");
    }

    // Get the requests in parallel
    var random = new Random();
    Parallel.ForEach(urls,new ParallelOptions { MaxDegreeOfParallelism = 3 },url =>
    {
        try
        {
            var client = new WebClient();
            var jsonString = client.DownloadString(url);
            File.WriteallText($@"C:\temp\parallel_requests\response_{DateTime.Now:yyyy.MM.dd_HH-mm-ss.fff}_{random.Next(10000,99999)}.ok.json",$"{url}\n{jsonString}");
        }
        catch (Exception err)
        {
            File.WriteallText($@"C:\temp\parallel_requests\response_{DateTime.Now:yyyy.MM.dd_HH-mm-ss.fff}_{random.Next(10000,99999)}.err.txt",$"{url}\n{err.Message}");
        }
    });

    Debugger.Break();
}

这是2个屏幕快照,其中有两种情况的简短摘要

.Net Core 3.1

.Net Framework 4.7.2

解决方法

感谢@NikitaChayka的提示。

使用全局HttpClient并将Parallel.ForEach更改为AsParallel()。ForAll()解决了该问题。

static void Main(string[] args)
{
    if (!Directory.Exists(@"C:\temp\parallel_requests"))
        Directory.CreateDirectory(@"C:\temp\parallel_requests");

    // List with the request urls (simulated,in real life these urls will be the ones that we need)
    var urls = new List<string>();
    for (var i = 0; i < 4000; i++)
    {
        urls.Add(@"https://jsonplaceholder.typicode.com/todos/1");
    }

    // Get the requests in parallel
    var client = new HttpClient();
    urls.AsParallel().ForAll(url =>
    {
        try
        {
            var response = client.GetAsync(url).Result;
            response.EnsureSuccessStatusCode();
            var jsonString = response.Content.ReadAsStringAsync().Result;
            File.WriteAllText($@"C:\temp\parallel_requests\response_{DateTime.Now:yyyy.MM.dd_HH-mm-ss.fff}_{Guid.NewGuid()}.ok.json",$"{url}\n{jsonString}");
        }
        catch (Exception err)
        {
            File.WriteAllText($@"C:\temp\parallel_requests\response_{DateTime.Now:yyyy.MM.dd_HH-mm-ss.fff}_{Guid.NewGuid()}.err.txt",$"{url}\n{err.Message}");
        }
    });

    Debugger.Break();
}