【问题标题】:How to wait on all tasks (created task and subtask) without using TaskCreationOptions.AttachedToParent如何在不使用 TaskCreationOptions.AttachedToParent 的情况下等待所有任务(创建的任务和子任务)
【发布时间】:2012-07-05 18:12:13
【问题描述】:

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

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

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

服务器至少有8个核心,每个任务会创建2-3个子任务,所以如果我设置AttachedToParent选项,我的印象是第二个子任务不会在第一个子任务的三个任务结束。所以我将在这里进行有限的多任务处理。

所以有了这个进程树:

我的印象是,如果我在每次启动线程时设置 AttachedToParent 属性,B 不会在 E、F、G 完成之前结束,所以 C 将在 B 完成之前开始,而我将只有 3 个活动线程我可以拥有的 8 个。

如果我不放 AttachedToParent 属性,A 会很快完成并返回。

那么,如果我不设置此选项,我该如何确保我的 8 个内核始终得到充分利用?

【问题讨论】:

  • 不要依赖展示次数。要么在文档中查找它,要么自己尝试一下。

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


【解决方案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();

【讨论】:

    【解决方案2】:

    正如 Me.Name 所提到的,AttachedToParent 的行为与您的印象不同。我认为在这种情况下这是一个不错的选择。

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

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

    【讨论】:

      【解决方案3】:

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

      source(在接受的答案中)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-10
        • 2012-01-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多