13

AttachedToParentのパラメータの仕組みを理解することに問題があります。私が理解したようAttachedToParentタスクの混乱

public static void Main(string[] args) 
    { 
     Task<int[]> parentTask = Task.Run(()=> 
     { 
      int[] results = new int[3]; 

      Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent); 
      Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent); 
      Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent); 

      t1.Start(); 
      t2.Start(); 
      t3.Start(); 

      return results; 
     }); 

     Task finalTask = parentTask.ContinueWith(parent => 
     { 
      foreach (int result in parent.Result) 
      { 
       Console.WriteLine(result); 
      } 
     }); 

     finalTask.Wait(); 
     Console.ReadLine(); 
    } 

、タスクが子タスクを持っている場合、すべての子タスクが準備ができているとき、親はタスクの終了:ここ

は、サンプルコードです。この例の問題は、出力が次のようになっていることです。

0 
0 
0 

これは、親タスクが子タスクの完了を待っていないことを意味します。有効な結果0 1 2を取得する唯一の方法は、return results;文の前にこのようなコードのいくつかの作品を追加することによって、子供Taksのすべてに待って使用することです:

Task[] taskList = { t1, t2, t3 }; 
Task.WaitAll(taskList); 

私の質問はこれです。すべての子タスクに対して手動でWaitメソッドを呼び出さなければならないときは、なぜTaskCreationOptions.AttachedToParentを使用するのですか?

編集:私はこの質問を書いていたが

、私は、コードを少し変更しましたし、今AttachedToParentはうまく動作します。唯一の違いはTask.Run();の代わりにparentTask.Start();を使用したことです。

public static void Main(string[] args) 
    { 
     Task<int[]> parentTask = new Task<int[]>(()=> 
     { 
      int[] results = new int[3]; 

      Task t1 = new Task(() => { Thread.Sleep(3000); results[0] = 0; }, TaskCreationOptions.AttachedToParent); 
      Task t2 = new Task(() => { Thread.Sleep(3000); results[1] = 1; }, TaskCreationOptions.AttachedToParent); 
      Task t3 = new Task(() => { Thread.Sleep(3000); results[2] = 2; }, TaskCreationOptions.AttachedToParent); 

      t1.Start(); 
      t2.Start(); 
      t3.Start(); 

      //Task[] taskList = { t1, t2, t3 }; 
      //Task.WaitAll(taskList); 

      return results; 
     }); 

     parentTask.Start(); 

     Task finalTask = parentTask.ContinueWith(parent => 
     { 
      foreach (int result in parent.Result) 
      { 
       Console.WriteLine(result); 
      } 
     }); 

     finalTask.Wait(); 
     Console.ReadLine(); 
    } 

最初の例に問題がある理由はまだ分かりません。このブログ記事で

答えて

19

ルック:Task.Run vs Task.Factory.StartNew

最初の例:

Task.Run(someAction); 

は法と同等の簡素化されます。

Task.Factory.StartNew(someAction, 
     CancellationToken.None, 
     TaskCreationOptions.DenyChildAttach, 
     TaskScheduler.Default); 

私は反射板を使用して、少し研究を行って、ここにありますメソッドのソースTask.Run

public static Task Run(Func<Task> function, CancellationToken cancellationToken) 
    { 
     if (function == null) 
     throw new ArgumentNullException("function"); 
     cancellationToken.ThrowIfSourceDisposed(); 
     if (cancellationToken.IsCancellationRequested) 
     return Task.FromCancellation(cancellationToken); 
     else 
     return (Task) new UnwrapPromise<VoidTaskResult>(
      (Task) Task<Task>.Factory.StartNew(function, 
            cancellationToken, 
            TaskCreationOptions.DenyChildAttach, 
            TaskScheduler.Default), 
      true); 
    } 

メソッドTask.Factory.StartNewの重要なパラメータはTaskCreationOptions creationOptionsです。方法Task.Factory.StartNewでは、そのパラメータはTaskCreationOptions.DenyChildAttachに等しい。それは試みが作成したタスクは、コードの右の動作を実現するためにTaskCreationOptions.Noneに変更する必要があり

に子タスクを添付 に行われた場合

と、InvalidOperationExceptionがスローされることを意味します。

メソッドTask.Runは、TaskCreationOptionsパラメータを変更する機能を提供していません。

+0

* DenyChildAttach *が本当に問題であるようです。このような親タスクを作成すると、 'TaskFactory tf = new TaskFactory(TaskCreationOptions.DenyChildAttach、TaskContinuationOptions.None); タスク parentTask = tf.StartNew(()=> {....}); '同じ問題が発生し、* TaskCreationOptions.None *を使用するとうまく動作します。 –