场景是这样的,我手头有4个特定的URL,每个URL页面包含许多指向网页的链接,我需要提取这些网页的一些信息。我打算使用嵌套任务来完成这项工作,一个任务中包含多个任务。像下面这样。
3
这是我的问题:
var t1Actions = new List<Action>();
var t1 = Task.Factory.StartNew(() =>
{
foreach (var action in t1Actions)
{
Task.Factory.StartNew(action, TaskCreationOptions.AttachedToParent);
}
});
var t2Actions = new List<Action>();
var t2 = Task.Factory.StartNew(() =>
{
foreach (var action in t2Actions)
{
Task.Factory.StartNew(action, TaskCreationOptions.AttachedToParent);
}
});
var t3Actions = new List<Action>();
var t3 = Task.Factory.StartNew(() =>
{
foreach (var action in t3Actions)
{
Task.Factory.StartNew(action, TaskCreationOptions.AttachedToParent);
}
});
var t4Actions = new List<Action>();
var t4 = Task.Factory.StartNew(() =>
{
foreach (var action in t4Actions)
{
Task.Factory.StartNew(action, TaskCreationOptions.AttachedToParent);
}
});
Task.WhenAll(t1, t2, t3, t4);
还是保持原样?任何建议都会有所帮助。
答案 0 :(得分:0)
实际的问题不在于如何处理子任务。这是从某些目录页面获取URL列表,检索这些页面并进行处理的方法。
可以使用.NET的Dataflow库轻松完成此操作。每个步骤都可以实现为读取一个URL并产生输出的块。
例如:
var listBlock = new TransformManyBlock<Uri,Uri>(async uri=>
{
var content=await httpClient.GetStringAsync(uri);
var uris=ProcessThePage(contents);
return uris;
});
var downloadBlock = new TransformBlock<Uri,(Uri,string)>(async uri=>
{
var content=await httpClient.GetStringAsync(uri);
return (uri,content);
});
var processingBlock = new ActionBlock<(Uri uri,string content)>(async msg=>
{
//Do something
var pathFromUri(msg.uri);
File.WriteAllText(pathFromUri,msg.content);
});
var linkOptions=new DataflowLinkOptions{PropagateCompletion=true};
listBlock.LinkTo(downloadBlock,linkOptions);
downloadBlock.LinkTo(processingBlock,linkOptions);
每个块都使用其自己的Task运行。您可以指定一个块可以使用多个任务,例如,同时下载多个页面。
每个块都有一个输入和输出缓冲区。您可以为输入缓冲区指定一个限制,以避免在块中充满太多要处理的消息。如果一个块达到极限,上游块将暂停。这样,您可以防止downloadBlock
泛滥成千上万个页面的慢速processingBlock
。
一旦有了管道,就可以将消息发布到第一个块。完成后,您可以将代码块告知Complete()。流水线中的每个块将完成其输入缓冲区中的消息处理,并将完成调用传播到下一个链接的块。
您可以通过等待最后一个块的Completion
任务来等待所有消息完成。
var directoryPages=new Uri[]{..};
foreach(var uri in directoryPages)
{
listBlock.Post(uri);
}
listBlock.Complete();
await processingBlock.Complete();
ExecutionDataflowBlockOptions可用于指定多个任务的使用和输入缓冲区限制,例如:
var options=new ExecutionDataflowBlockOptions
{
BoundedCapacity=10,
MaxDegreeOfParallelism=4,
};
var downloadBlock = new TransformBlock<Uri,(Uri,string)>(...,options);
这意味着downloadBlock
在通知listBlock
暂停之前将接受多达10个URI。最多可同时处理4个Uris