5

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

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

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

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

所以有了这个进程树:

在此处输入图像描述

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

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

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

4

3 回答 3

2

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

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

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

于 2012-07-05T11:45:01.010 回答
2

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

来源(在接受的答案中)。

于 2012-07-05T11:07:50.070 回答
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();
于 2012-07-05T16:52:37.150 回答