如何在不使用TaskCreationOptions.AttachedToParent的情况下等待所有任务(创建的任务和子任务)

时间:2012-07-05 10:40:45

标签: c# .net parallel-processing task-parallel-library thread-synchronization

我将不得不创建一个创建多个Task的并发软件,每个Task都可以生成另一个任务(也可以生成另一个任务,......)。

我需要调用启动任务的方法阻塞:在完成所有任务和子任务之前不返回。

我知道有TaskCreationOptions.AttachedToParent属性,但我认为它不合适:

服务器至少会有8个核心,每个任务都会创建2-3个子任务,所以如果我设置AttachedToParent选项,我会觉得第二个子任务无法启动在第一个子任务的三个任务结束之前。所以我将在这里进行有限的多任务处理。

所以使用这个过程树:

enter image description here

我的印象是,如果我每次启动一个线程时都设置了AttachedToParent属性,那么在E,F,G完成之前B不会结束,所以C将在B完成之前启动,而我将只有3个活动线程代替我能拥有的8个。

如果我没有放置AttachedToParent属性,A将很快完成并返回。

那么如果我没有设置此选项,我怎么能确保我总是使用我的8个内核呢?

3 个答案:

答案 0 :(得分:2)

TaskCreationOptions.AttachedToParent不会阻止其他子任务启动,而是阻止父任务本身关闭。因此,当E,F和G以AttachedToParent启动时,B不会被标记为已完成,直到完成所有三个。所以它应该按照你想要的那样做。

source(在接受的答案中)。

答案 1 :(得分:2)

正如Me.Name所述,AttachedToParent根据您的印象不符合行为。我认为在这种情况下这是一个很好的选择。

但是如果您不想出于某种原因使用它,您可以等待所有子任务完成Task.WaitAll()。虽然这意味着你必须将所有这些都放在一个集合中。

Task.WaitAll()阻止当前线程,直到所有Task完成。如果您不希望这样,并且您使用的是.Net 4.5,则可以使用Task.WhenAll(),这将返回一个Task,当所有给定的Task完成时,它将完成。

答案 2 :(得分: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();