Task<T> 个对象的并行处理

Parallel processing of Task<T> objects

我正在从原始 URL 开始抓取网页并递归地跟踪任何链接以确定这些网页上列出的电子邮件地址。我使用 VS2015 和 .net 4.6 来利用通过线程提供的抽象/简单任务。

我已经递归地下载了页面,但应用程序似乎仍然存在严重的瓶颈。使用下面的简单代码,如何使流程更能够并行处理每个网页以查询电子邮件的内容,以及随后的 url 链接?

似乎可以串联启动任务,这样在一个页面上找到的所有 url 都可以同时添加到循环逻辑的下一次迭代中?还是任务现在在幕后处理这个问题?

下面是我的代码,请提供一些解释,以便我可以更好地理解解决方案,因为我刚刚开始执行任务。 (代码正在使用 HTML 敏捷包)

List<PageEmail> lstEmailData = new List<PageEmail>();

private void startButton_Click(object sender, RoutedEventArgs e)
{
    getWEbData("http://localhost:801/"); //starting url
}

private async void getWEbData(string url) {
    Task<string> getHTMLTask = AccessTheWebAsync(url);
    string PageData = await getHTMLTask;

    var html = new HtmlDocument();
    html.LoadHtml  (PageData);

    var emails = html.DocumentNode.SelectNodes("//a[@href]")
      .Select(a => a.Attributes["href"].Value)
      .Where(href => href.StartsWith("mailto:")) // keep emails, skipp links
      .ToList();

    lstEmailData.Add(new PageEmail(url, emails));

    var urls = html.DocumentNode.SelectNodes("//a[@href]")
     .Select(a => a.Attributes["href"].Value)
     .Where(href => !href.StartsWith("mailto:")) // skip emails, find only url links
     .ToList();

    foreach (string s in urls) {
        getWEbData(s);
    }

}

async Task<string> AccessTheWebAsync(string URL)
{
    HttpClient client = new HttpClient() { MaxResponseContentBufferSize = 1000000 };
    Task<string> getStringTask = client.GetStringAsync(URL);
    return await getStringTask;
}

核心问题可能是您受限于您无法控制的远程服务器。您也有可能受到 .NET 中(旧的)默认连接限制的限制;尝试在流程开始时将 ServicePointManager.DefaultConnectionLimit 设置为 int.MaxValue。除此之外,您无能为力。

既然你在学习async,你应该知道最好避免async void。理想情况下,getWEbData 应该 return 和 Task,并且此更改允许您将 "child" URL 视为 "child" 任务。这对于正确传播错误特别有用:

private async void startButton_Click(object sender, RoutedEventArgs e)
{
  await GetWebDataAsync("http://localhost:801/"); //starting url
}

private async Task GetWebDataAsync(string url) {
  var urls = ...;

  var urlTasks = urls.Select(s => GetWebDataAsync(s));
  await Task.WhenAll(urlTasks);
}