如何等待所有任务(包括创建的任务和子任务)而不使用TaskCreationOptions.AttachedToParent。

5
我需要创建一个并发软件,可以创建多个任务,并且每个任务都可以生成另一个任务(也可以生成另一个任务,以此类推)。
我需要在调用启动任务的方法之前进行阻塞:在所有任务和子任务完成之前不返回。
我知道有这个TaskCreationOptions.AttachedToParent属性,但我认为它不适合:
服务器将至少有8个核心,每个任务将创建2-3个子任务,因此如果我设置AttachedToParent选项,我有一种印象,即第二个子任务将不会在第一个子任务的三个任务结束之前启动。因此,我在此处会有限制的多任务处理。
因此,对于这个进程树:
我有一种印象,如果我每次启动线程时都设置AttachedToParent属性,则B将在E、F、G完成之前不会结束,因此C将在B完成之前开始,我只能使用3个活动线程而不是可以使用的8个线程。
如果我不设置AttachedToParent属性,则A将非常快地完成并返回。
那么如果我不设置此选项,如何确保始终使用我的8个核心?

3
不要依赖印象。要么查阅文档,要么自己试一下。 - svick
3个回答

2
TaskCreationOptions.AttachedToParent 并不能阻止其他子任务的启动,而是防止父任务本身关闭。当使用 AttachedToParent 启动 E、F 和 G 时,只有在所有三个任务都完成后,B 才会被标记为已完成。所以它应该正好符合您的要求。
参考来源(在被接受的答案中)。

2

正如Me.Name所提到的,AttachedToParent并不按照你的想象行事。我认为在这种情况下它是一个很好的选择。

但如果由于任何原因您不想使用它,您可以使用Task.WaitAll()等待所有子任务完成。虽然这意味着您必须将它们全部放入集合中。

Task.WaitAll()阻止当前线程直到所有Task完成。如果您不想要这个且您正在使用.Net 4.5,则可以使用Task.WhenAll(),它将返回一个单一的Task,该任务将在给定的所有Task全部完成时结束。


1
您可以使用TaskFactory创建类似于以下示例的选项:
Task parent = new Task(() => { 
var cts = new CancellationTokenSource(); 
var tf = new TaskFactory<Int32>(cts.Token,  
                                        TaskCreationOptions.AttachedToParent,  
                                        TaskContinuationOptions.ExecuteSynchronously,  
TaskScheduler.Default); 

 // This tasks creates and starts 3 child tasks 
 var childTasks = new[] { 
       tf.StartNew(() => Sum(cts.Token, 10000)), 
       tf.StartNew(() => Sum(cts.Token, 20000)), 
       tf.StartNew(() => Sum(cts.Token, Int32.MaxValue))  // Too big, throws Overflow
 }; 

// If any of the child tasks throw, cancel the rest of them 
for (Int32 task = 0; task <childTasks.Length; task++) 
  childTasks[task].ContinueWith( 
     t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted); 

// When all children are done, get the maximum value returned from the  
// non-faulting/canceled tasks. Then pass the maximum value to another  
// task which displays the maximum result 
tf.ContinueWhenAll( 
   childTasks,  
   completedTasks => completedTasks.Where( 
     t => !t.IsFaulted && !t.IsCanceled).Max(t => t.Result), CancellationToken.None) 
   .ContinueWith(t =>Console.WriteLine("The maximum is: " + t.Result), 
      TaskContinuationOptions.ExecuteSynchronously); 
}); 

// When the children are done, show any unhandled exceptions too 
parent.ContinueWith(p => { 
    // I put all this text in a StringBuilder and call Console.WriteLine just once  
    // because this task could execute concurrently with the task above & I don't  
    // want the tasks' output interspersed 
    StringBuildersb = new StringBuilder( 
                      "The following exception(s) occurred:" + Environment.NewLine); 

    foreach (var e in p.Exception.Flatten().InnerExceptions)  
         sb.AppendLine("   "+ e.GetType().ToString()); 

    Console.WriteLine(sb.ToString()); 
  }, TaskContinuationOptions.OnlyOnFaulted);

  // Start the parent Task so it can start its children 
  parent.Start();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接