c# - 父任务不等待子任务

标签 c# multithreading task wait

首先,我对线程非常陌生。我想要完成的是,有一个 url 列表,我正在尝试抓取(我认为)并检测 flash(如果该 url 包含它),而且这也是并行的。有些网址具有外部链接,这意味着我必须生成一个新任务并递归调用我的方法。我想使用 AsParallel 或 Parallel.ForEach,但它们不接受任务输入。

所以我有两个问题。 1)我想要一种更好的方法来并行爬行url(如果需要则递归) 2)如果我写的是一个好方法,那么我应该怎么做才能让任务等待它的子任务?

附注我搜索了 stackoverflow,但找不到我需要的东西。抱歉,如果我错过了有人已经回答了与我类似的问题。

    async Task CrawlAndDetectFlash(LearningResource resource, string url, int depth)
    {
        using (var client = new HttpClient())
        using (var response = await client.GetAsync(url))
        {
            response.EnsureSuccessStatusCode();
            using (var content = response.Content)
            {
                var result = content.ReadAsStringAsync().Result;
                resource.FlashRequired = result.Contains("application/x-shockwave-flash") || result.Contains("application/x-director") || result.Contains(".swf") ? 1 : 0;
                if (resource.FlashRequired == 0 && depth == 1)
                {
                    var document = new HtmlDocument();
                    document.LoadHtml(result);
                    var links = document.DocumentNode.Descendants("a")
                        .Where(a => a.Attributes.Contains("class") && String.Equals(a.GetAttributeValue("class", string.Empty), "external"))
                        .Select(a => a.GetAttributeValue("href", null))
                        .Distinct()
                        .Where(u => !String.IsNullOrEmpty(u))
                        .ToList();
                    if (links.Count > 0)
                    {
                        foreach (var link in links)
                        {
                            Task child = CrawlAndDetectFlash(resource, link, 2);
                            child.Wait();
                        }
                    }
                }
            }
        }
    }

最佳答案

首先,需要区分“并发”、“并行”和“异步”。并发是指一次做不止一件事;并行性是使用多线程的并发形式;异步是一种没有线程的并发形式。当您想要将线程分布在多个 CPU 核心上时,并行性最适合 CPU 密集型代码。当您不想阻塞线程时,异步最适合 I/O 绑定(bind)代码。

就您而言,看起来您主要受 I/O 限制,因此异步是最佳选择。这意味着AsParallelParallel.ForEach是这个问题的不正确的解决方案(它们是并行的,而不是异步的)。

下一课(正如我在博客上所描述的)是你 don't want to block on asynchronous code ;阻塞破坏了异步的全部意义。所以Task<T>.ResultTask.Wait不应该使用。而不是这些,只需使用 await :

async Task CrawlAndDetectFlashAsync(LearningResource resource, string url, int depth)
{
  using (var client = new HttpClient())
  using (var response = await client.GetAsync(url))
  {
    response.EnsureSuccessStatusCode();
    using (var content = response.Content)
    {
      var result = await content.ReadAsStringAsync(); // Result -> await
      resource.FlashRequired = result.Contains("application/x-shockwave-flash") || result.Contains("application/x-director") || result.Contains(".swf") ? 1 : 0;
      if (resource.FlashRequired == 0 && depth == 1)
      {
        var document = new HtmlDocument();
        document.LoadHtml(result);
        var links = document.DocumentNode.Descendants("a")
                    .Where(a => a.Attributes.Contains("class") && String.Equals(a.GetAttributeValue("class", string.Empty), "external"))
                    .Select(a => a.GetAttributeValue("href", null))
                    .Distinct()
                    .Where(u => !String.IsNullOrEmpty(u))
                    .ToList();
        if (links.Count > 0)
        {
          foreach (var link in links)
          {
            Task child = CrawlAndDetectFlashAsync(resource, link, 2);
            await child; // Wait -> await
          }
        }
      }
    }
  }
}

既然该方法是正确异步的,您可以考虑添加更多并发性。例如,如果您想同时处理所有子链接,则 foreach循环可以重写为:

if (links.Count > 0)
{
  var childTasks = links.Select(x => CrawlAndDetectFlashAsync(resource, x, 2)).ToList();
  await Task.WhenAll(childTasks);
}

关于c# - 父任务不等待子任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34170575/

相关文章:

c# - 在多个 HttpWebRequest 中使用相同的 CookieContainer 是否安全?

C# 任务返回输出

android - 在Android中,Task == Application?

c# - 如何等待有条件的任务?

Django + 执行异步进程?

c# - Visual Studio 2015 SQL Server Data Tools 缺少 "Add Table"选项

c# - 在特定位置的 xml 中添加文本行?

ruby - (J)Ruby - 杀死一个线程可以吗?

c# - 如果 boolean 为 false,则在字符串中添加逗号和其他单词

java - 在Java中,如何将对象从工作线程传递回主线程?