方法: 子タスクがその親にアタッチしないようにするHow to: Prevent a Child Task from Attaching to its Parent

このドキュメントでは、子タスクが親タスクにアタッチしないようにする方法を紹介します。This document demonstrates how to prevent a child task from attaching to the parent task. サード パーティによって書き込まれ、タスクも使用するコンポーネントを呼び出す場合は、子タスクがその親にアタッチしないようにすると便利です。Preventing a child task from attaching to its parent is useful when you call a component that is written by a third party and that also uses tasks. たとえば、TaskCreationOptions.AttachedToParent オプションを使用して Task または Task<TResult> オブジェクトを作成するサード パーティ コンポーネントが長時間実行されているか、ハンドルされない例外をスローする場合、コードで問題が発生する可能性があります。For example, a third-party component that uses the TaskCreationOptions.AttachedToParent option to create a Task or Task<TResult> object can cause problems in your code if it is long-running or throws an unhandled exception.

Example

次の例では、子タスクが親にアタッチしないようにする効果と、既定のオプションを使用する効果を比較します。The following example compares the effects of using the default options to the effects of preventing a child task from attaching to the parent. この例では、Task オブジェクトも使用する、サード パーティ ライブラリを呼び出す、Task オブジェクトを作成します。The example creates a Task object that calls into a third-party library that also uses a Task object. サード パーティ ライブラリでは AttachedToParent オプションを使用して、Task オブジェクトを作成します。The third-party library uses the AttachedToParent option to create the Task object. アプリケーションでは TaskCreationOptions.DenyChildAttach オプションを使用して、親タスクを作成します。The application uses the TaskCreationOptions.DenyChildAttach option to create the parent task. このオプションは、子タスクの AttachedToParent 指定を削除するようにランタイムに指示します。This option instructs the runtime to remove the AttachedToParent specification in child tasks.

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

// Defines functionality that is provided by a third-party.
// In a real-world scenario, this would likely be provided
// in a separate code file or assembly.
namespace Contoso
{
   public class Widget
   {     
      public Task Run()
      {
         // Create a long-running task that is attached to the 
         // parent in the task hierarchy.
         return Task.Factory.StartNew(() =>
         {
            // Simulate a lengthy operation.
            Thread.Sleep(5000);

         }, TaskCreationOptions.AttachedToParent);
      }
   }
}

// Demonstrates how to prevent a child task from attaching to the parent.
class DenyChildAttach
{
   static void RunWidget(Contoso.Widget widget,
      TaskCreationOptions parentTaskOptions)
   {
      // Record the time required to run the parent
      // and child tasks.
      Stopwatch stopwatch = new Stopwatch();
      stopwatch.Start();

      Console.WriteLine("Starting widget as a background task...");

      // Run the widget task in the background.
      Task<Task> runWidget = Task.Factory.StartNew(() =>
         {
            Task widgetTask = widget.Run();

            // Perform other work while the task runs...
            Thread.Sleep(1000);

            return widgetTask;
         }, parentTaskOptions);
      
      // Wait for the parent task to finish.
      Console.WriteLine("Waiting for parent task to finish...");
      runWidget.Wait();
      Console.WriteLine("Parent task has finished. Elapsed time is {0} ms.", 
         stopwatch.ElapsedMilliseconds);

      // Perform more work...
      Console.WriteLine("Performing more work on the main thread...");
      Thread.Sleep(2000);
      Console.WriteLine("Elapsed time is {0} ms.", stopwatch.ElapsedMilliseconds);

      // Wait for the child task to finish.
      Console.WriteLine("Waiting for child task to finish...");
      runWidget.Result.Wait();
      Console.WriteLine("Child task has finished. Elapsed time is {0} ms.",
        stopwatch.ElapsedMilliseconds);
   }
     
   static void Main(string[] args)
   {
      Contoso.Widget w = new Contoso.Widget();

      // Perform the same operation two times. The first time, the operation
      // is performed by using the default task creation options. The second
      // time, the operation is performed by using the DenyChildAttach option
      // in the parent task.

      Console.WriteLine("Demonstrating parent/child tasks with default options...");
      RunWidget(w, TaskCreationOptions.None);

      Console.WriteLine();

      Console.WriteLine("Demonstrating parent/child tasks with the DenyChildAttach option...");
      RunWidget(w, TaskCreationOptions.DenyChildAttach);
   }
}

/* Sample output:
Demonstrating parent/child tasks with default options...
Starting widget as a background task...
Waiting for parent task to finish...
Parent task has finished. Elapsed time is 5014 ms.
Performing more work on the main thread...
Elapsed time is 7019 ms.
Waiting for child task to finish...
Child task has finished. Elapsed time is 7019 ms.

Demonstrating parent/child tasks with the DenyChildAttach option...
Starting widget as a background task...
Waiting for parent task to finish...
Parent task has finished. Elapsed time is 1007 ms.
Performing more work on the main thread...
Elapsed time is 3015 ms.
Waiting for child task to finish...
Child task has finished. Elapsed time is 5015 ms.
*/
Imports System
Imports System.Diagnostics
Imports System.Threading
Imports System.Threading.Tasks

' Defines functionality that is provided by a third-party.
' In a real-world scenario, this would likely be provided
' in a separate code file or assembly.
Namespace Contoso
   Public Class Widget
      Public Function Run() As Task
         ' Create a long-running task that is attached to the 
         ' parent in the task hierarchy.
         Return Task.Factory.StartNew(Sub() Thread.Sleep(5000), TaskCreationOptions.AttachedToParent)
            ' Simulate a lengthy operation.
      End Function
   End Class
End Namespace

' Demonstrates how to prevent a child task from attaching to the parent.
Friend Class DenyChildAttach
   Private Shared Sub RunWidget(ByVal widget As Contoso.Widget, ByVal parentTaskOptions As TaskCreationOptions)
      ' Record the time required to run the parent
      ' and child tasks.
      Dim stopwatch As New Stopwatch()
      stopwatch.Start()

      Console.WriteLine("Starting widget as a background task...")

      ' Run the widget task in the background.
      Dim runWidget As Task(Of Task) = Task.Factory.StartNew(Function()
            ' Perform other work while the task runs...
          Dim widgetTask As Task = widget.Run()
          Thread.Sleep(1000)
          Return widgetTask
      End Function, parentTaskOptions)

      ' Wait for the parent task to finish.
      Console.WriteLine("Waiting for parent task to finish...")
      runWidget.Wait()
      Console.WriteLine("Parent task has finished. Elapsed time is {0} ms.", stopwatch.ElapsedMilliseconds)

      ' Perform more work...
      Console.WriteLine("Performing more work on the main thread...")
      Thread.Sleep(2000)
      Console.WriteLine("Elapsed time is {0} ms.", stopwatch.ElapsedMilliseconds)

      ' Wait for the child task to finish.
      Console.WriteLine("Waiting for child task to finish...")
      runWidget.Result.Wait()
      Console.WriteLine("Child task has finished. Elapsed time is {0} ms.", stopwatch.ElapsedMilliseconds)
   End Sub

   Shared Sub Main(ByVal args() As String)
      Dim w As New Contoso.Widget()

      ' Perform the same operation two times. The first time, the operation
      ' is performed by using the default task creation options. The second
      ' time, the operation is performed by using the DenyChildAttach option
      ' in the parent task.

      Console.WriteLine("Demonstrating parent/child tasks with default options...")
      RunWidget(w, TaskCreationOptions.None)

      Console.WriteLine()

      Console.WriteLine("Demonstrating parent/child tasks with the DenyChildAttach option...")
      RunWidget(w, TaskCreationOptions.DenyChildAttach)
   End Sub
End Class

' Sample output:
'Demonstrating parent/child tasks with default options...
'Starting widget as a background task...
'Waiting for parent task to finish...
'Parent task has finished. Elapsed time is 5014 ms.
'Performing more work on the main thread...
'Elapsed time is 7019 ms.
'Waiting for child task to finish...
'Child task has finished. Elapsed time is 7019 ms.
'
'Demonstrating parent/child tasks with the DenyChildAttach option...
'Starting widget as a background task...
'Waiting for parent task to finish...
'Parent task has finished. Elapsed time is 1007 ms.
'Performing more work on the main thread...
'Elapsed time is 3015 ms.
'Waiting for child task to finish...
'Child task has finished. Elapsed time is 5015 ms.
'

親タスクはすべての子タスクが終了するまで終了しないため、長時間実行される子タスクによって、アプリケーション全体のパフォーマンスが低下する場合があります。Because a parent task does not finish until all child tasks finish, a long-running child task can cause the overall application to perform poorly. この例では、アプリケーションが既定のオプションを使用して親タスクを作成する場合に、親タスクが終了する前に子タスクが終了する必要があります。In this example, when the application uses the default options to create the parent task, the child task must finish before the parent task finishes. アプリケーションで TaskCreationOptions.DenyChildAttach オプションを使用する場合、子は親にアタッチされません。When the application uses the TaskCreationOptions.DenyChildAttach option, the child is not attached to the parent. そのため、アプリケーションは、親タスクの終了後、子タスクが終了するのを待機する前に、追加の作業を実行できます。Therefore, the application can perform additional work after the parent task finishes and before it must wait for the child task to finish.

関連項目See also