gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 21:53:07 25 4
gpt4 key购买 nike

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

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

附言我搜索了 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 绑定(bind)代码。当您不想阻塞线程时,异步最适合 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com