2017-08-10 80 views
0

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

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的解决方案,但我不知道如何把await关键字在我的主要方法,目前该程序简单地退出,因为它缺乏Task.Run()之前。正如你所看到的,我已经应用了一个关于async Download()方法的解决方法,以main方法调用它。 我也怀疑在不同的并行线程中使用同一个httpclient实例。请告诉我我是否应该每次创建httpclient的新实例。

回答

0

你是对的,你必须阻止任务在控制台应用程序,否则该程序将在它完成之前退出。但是你这样做比你需要的要多。旨在阻止主线程,并将其余部分委托给方法async。一个好的做法是创建一个方法为署名像private async Task MainAsyc(args),把你的程序逻辑的“胆”有,从Main这样称呼它:

MainAsync(args).Wait(); 

在你的榜样,将一切从MainMainAsync。然后,您可以随意使用awaitTask.RunParallel.For明确地消耗了I/O绑定工作的新线程,这在异步世界中是不必要的。改为使用Task.WhenAll。您MainAsync方法的最后一部分应该结束了看起来像这样:

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是异步世界中节流并行的一个很好的库。 Here's a simple example

关于HttpClient,跨线程使用单个实例是completely safehighly encouraged

+0

谢谢。我使用另一个包中的异步foreach循环来解决它(使用类似问题的其他答案之一)。在为每个新的线程调用使用一个新的http客户端实例后,我观察到速度的显着提高。 –

+0

嗯。我非常怀疑HttpClient的许多实例是加速的原因。其他东西必须有所不同。我会告诫你,你所做的并不是最佳的,但如果你不[用尽套接字](https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/)我想没关系。 –