Parallel.For 和 httpclient 使应用程序 C# 崩溃

Parallel.For and httpclient crash the application C#

我想避免由于并行 for 循环和 httpclient 而导致的应用程序崩溃问题,但由于我的编程知识有限,我无法应用网络上其他地方提供的解决方案。我的代码粘贴在下面。

class Program
    {
        public static List<string> words = new List<string>();
        public static int count = 0;
        public static string output = "";
        private static HttpClient Client = new HttpClient();
        public static void Main(string[] args)
        {
            //input path strings...
            List<string> links = new List<string>();
            links.AddRange(File.ReadAllLines(input));
            List<string> longList = new List<string>(File.ReadAllLines(@"a.txt"));
            words.AddRange(File.ReadAllLines(output1));
            System.Net.ServicePointManager.DefaultConnectionLimit = 8;
            count = longList.Count;
            //for (int i = 0; i < longList.Count; i++)
            Task.Run(() => Parallel.For(0, longList.Count, new ParallelOptions { MaxDegreeOfParallelism = 5 }, (i, loopState) =>
            {
                Console.WriteLine(i);
                string link = @"some link" + longList[i] + "/";
                try
                {
                    if (!links.Contains(link))
                    {
                        Task.Run(async () => { await Download(link); }).Wait();
                    }
                }
                catch (System.Exception e)
                {

                }
                               }));
            //}

        }
        public static async Task Download(string link)
        {
            HtmlAgilityPack.HtmlDocument document = new HtmlDocument();
            document.LoadHtml(await getURL(link));
            //...stuff with html agility pack
        }
        public static async Task<string> getURL(string link)
        {
            string result = "";
            HttpResponseMessage response = await Client.GetAsync(link);
            Console.WriteLine(response.StatusCode);
            if(response.IsSuccessStatusCode)
            {
                HttpContent content = response.Content;
                var bytes = await response.Content.ReadAsByteArrayAsync();
                result = Encoding.UTF8.GetString(bytes);
            }
            return result;
        }

    }

有解决方案,例如 this one,但我不知道如何在我的 main 方法中放置 await 关键字,目前程序由于在 [=13] 之前不存在而直接退出=].如您所见,我已经应用了关于 async Download() 方法的变通方法以在 main 方法中调用它。 我也怀疑在不同的并行线程中使用相同的 httpclient 实例。请告诉我是否应该每次都创建新的 httpclient 实例。

你是对的,你必须在控制台应用程序中的某个地方阻止任务,否则程序将在完成之前退出。但是你做的比你需要做的更多。旨在仅阻塞主线程并将其余部分委托给 async 方法。一个好的做法是创建一个带有 private async Task MainAsyc(args) 签名的方法,将程序逻辑的 "guts" 放在那里,从 Main 调用它,如下所示:

MainAsync(args).Wait();

在您的示例中,将所有内容从 Main 移动到 MainAsync。然后你就可以随意使用 await 了。 Task.RunParallel.For 显式地为 I/O 绑定工作消耗新线程,这在异步世界中是不必要的。请改用 Task.WhenAllMainAsync 方法的最后一部分应该看起来像这样:

await Task.WhenAll(longList.Select(async s => {
    Console.WriteLine(i);
    string link = @"some link" + s + "/";
    try
    {
        if (!links.Contains(link))
        {
            await Download(link);
        }
    }
    catch (System.Exception e)
    {

    }
}));

虽然这里有一个小问题。您的示例将并行度限制在 5。如果您发现您仍然需要它,TPL Dataflow 是一个很棒的库,用于在异步世界中限制并行度。 .

关于HttpClient,跨线程使用单实例是completely safe and highly encouraged