带有子任务数量的任务

时间:2018-07-18 13:42:52

标签: c# .net multithreading task task-parallel-library

场景是这样的,我手头有4个特定的URL,每个URL页面包含许多指向网页的链接,我需要提取这些网页的一些信息。我打算使用嵌套任务来完成这项工作,一个任务中包含多个任务。像下面这样。

3

这是我的问题:

  1. 这是做我上面提到的工作的好方法吗?
  2. 哪个是高效的,将子任务替换为 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); 还是保持原样?
  3. 如果嵌套任务完成,我应该如何通知(例如UI),我是否可以控制嵌套任务?

任何建议都会有所帮助。

1 个答案:

答案 0 :(得分:0)

实际的问题不在于如何处理子任务。这是从某些目录页面获取URL列表,检索这些页面并进行处理的方法。

可以使用.NET的Dataflow库轻松完成此操作。每个步骤都可以实现为读取一个URL并产生输出的块。

  1. 第一个块可以是TransformManyBlock,它接受​​一个页面URL并返回一个页面URL列表
  2. 第二个块可以是TransformBlock,它接受​​单个页面URL并返回其内容
  3. 第三个块可以是一个接受页面并执行其所需操作的操作块。

例如:

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