Angefügte und getrennte untergeordnete AufgabenAttached and Detached Child Tasks

Eine untergeordnete Aufgabe (oder geschachtelte Aufgabe) ist eine System.Threading.Tasks.Task-Instanz, die im Benutzerdelegaten einer anderen Aufgabe erstellt wird, die als übergeordnete Aufgabe bezeichnet wird.A child task (or nested task) is a System.Threading.Tasks.Task instance that is created in the user delegate of another task, which is known as the parent task. Eine untergeordnete Aufgabe kann entweder getrennt oder angefügt werden.A child task can be either detached or attached. Eine getrennte untergeordnete Aufgabe ist eine Aufgabe, die unabhängig von der übergeordneten ausgeführt wird.A detached child task is a task that executes independently of its parent. Eine angefügte ungeordnete Aufgabe ist eine geschachtelte Aufgabe, die mit der Option TaskCreationOptions.AttachedToParent erstellt wird, deren übergeordnete Aufgabe nicht explizit oder standardmäßig verhindert, dass die Aufgabe angefügt wird.An attached child task is a nested task that is created with the TaskCreationOptions.AttachedToParent option whose parent does not explicitly or by default prohibit it from being attached. Eine Aufgabe kann beliebig viele angefügte oder getrennte untergeordnete Aufgaben erstellen. Die Anzahl wird lediglich durch die Systemressourcen beschränkt.A task may create any number of attached and detached child tasks, limited only by system resources.

In der folgenden Tabelle sind die grundlegenden Unterschiede zwischen den zwei Arten von untergeordneten Aufgaben aufgeführt.The following table lists the basic differences between the two kinds of child tasks.

KategorieCategory Getrennte untergeordnete AufgabenDetached child tasks Angefügte untergeordnete AufgabenAttached child tasks
Das übergeordnete Element wartet auf den Abschluss der untergeordneten Aufgaben.Parent waits for child tasks to complete. NeinNo JaYes
Das übergeordnete Element gibt von untergeordneten Aufgaben ausgelöste Ausnahmen weiter.Parent propagates exceptions thrown by child tasks. NeinNo JaYes
Der Status des übergeordneten Elements hängt vom Status des untergeordneten Elements ab.Status of parent depends on status of child. NeinNo JaYes

In den meisten Szenarios empfiehlt sich jedoch die Verwendung von getrennten untergeordneten Aufgabe, da die Beziehungen zu anderen Aufgaben weniger komplex sind.In most scenarios, we recommend that you use detached child tasks, because their relationships with other tasks are less complex. Aus diesem Grund werden in übergeordneten Aufgaben erstellte Aufgaben standardmäßig getrennt. Um die angefügte untergeordnete Aufgabe zu erstellen, müssen Sie die TaskCreationOptions.AttachedToParent-Option explizit angeben.That is why tasks created inside parent tasks are detached by default, and you must explicitly specify the TaskCreationOptions.AttachedToParent option to create an attached child task.

Getrennte untergeordnete AufgabenDetached child tasks

Obwohl eine untergeordnete Aufgabe durch eine übergeordnete Aufgabe erstellt wird, hängt diese standardmäßig von der übergeordneten Aufgabe ab.Although a child task is created by a parent task, by default it is independent of the parent task. Im folgenden Beispiel erstellt eine übergeordnete Aufgabe eine einfache untergeordnete Aufgabe:In the following example, a parent task creates one simple child task. Wenn Sie den Beispielcode mehrmals ausführen, stellen Sie möglicherweise fest, dass sich die Ausgabe von der Ausgabe im Beispiel unterscheidet und dass die Ausgabe sich möglicherweise bei jeder Ausführung des Codes ändert.If you run the example code multiple times, you may notice that the output from the example differs from that shown, and also that the output may change each time you run the code. Dies liegt daran, dass die übergeordnete und die untergeordnete Aufgabe unabhängig voneinander ausgeführt werden; das untergeordnete Element ist eine getrennte Aufgabe.This occurs because the parent task and child tasks execute independently of each other; the child is a detached task. Im Beispiel wird nur auf den Abschluss der übergeordneten Aufgabe gewartet, und die untergeordnete Aufgabe wird möglicherweise nicht ausgeführt oder abgeschlossen, ehe die Konsolen-App beendet wird.The example waits only for the parent task to complete, and the child task may not execute or complete before the console app terminates.

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

public class Example
{
   public static void Main()
   {
      var parent = Task.Factory.StartNew(() => {
         Console.WriteLine("Outer task executing.");

         var child = Task.Factory.StartNew(() => {
            Console.WriteLine("Nested task starting.");
            Thread.SpinWait(500000);
            Console.WriteLine("Nested task completing.");
         });
      });

      parent.Wait();
      Console.WriteLine("Outer has completed.");
   }
}
// The example produces output like the following:
//        Outer task executing.
//        Nested task starting.
//        Outer has completed.
//        Nested task completing.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task.Factory.StartNew(Sub()
                                            Console.WriteLine("Outer task executing.")
                                            Dim child = Task.Factory.StartNew(Sub()
                                                                                 Console.WriteLine("Nested task starting.")
                                                                                 Thread.SpinWait(500000)
                                                                                 Console.WriteLine("Nested task completing.")
                                                                              End Sub)
                                         End Sub)
      parent.Wait()
      Console.WriteLine("Outer task has completed.")
    End Sub
End Module
' The example produces output like the following:
'   Outer task executing.
'   Nested task starting.
'   Outer task has completed.
'   Nested task completing.

Wenn die untergeordnete Aufgabe durch ein Task<TResult>-Objekt statt einem Task-Objekt dargestellt wird, können Sie sicherstellen, dass die übergeordnete Aufgabe auf das Abschließen der Aufgabe des untergeordneten Elements wartet, indem Sie auf die Task<TResult>.Result-Eigenschaft des untergeordneten Elements zugreift, auch wenn es sich dabei um eine getrennte untergeordnete Aufgabe handelt.If the child task is represented by a Task<TResult> object rather than a Task object, you can ensure that the parent task will wait for the child to complete by accessing the Task<TResult>.Result property of the child even if it is a detached child task. Die Result-Eigenschaft blockiert, wie im folgenden Beispiel gezeigt, bis die Aufgabe abgeschlossen ist.The Result property blocks until its task completes, as the following example shows.

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

class Example
{
   static void Main()
   {
      var outer = Task<int>.Factory.StartNew(() => {
            Console.WriteLine("Outer task executing.");

            var nested = Task<int>.Factory.StartNew(() => {
                  Console.WriteLine("Nested task starting.");
                  Thread.SpinWait(5000000);
                  Console.WriteLine("Nested task completing.");
                  return 42;
            });

            // Parent will wait for this detached child.
            return nested.Result;
      });

      Console.WriteLine("Outer has returned {0}.", outer.Result);
   }
}
// The example displays the following output:
//       Outer task executing.
//       Nested task starting.
//       Nested task completing.
//       Outer has returned 42.
Imports System
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task(Of Integer).Factory.StartNew(Function()
                                                        Console.WriteLine("Outer task executing.")
                                                        Dim child = Task(Of Integer).Factory.StartNew(Function()
                                                                                                         Console.WriteLine("Nested task starting.")
                                                                                                         Thread.SpinWait(5000000)
                                                                                                         Console.WriteLine("Nested task completing.")
                                                                                                         Return 42
                                                                                                      End Function)
                                                        Return child.Result


                                                     End Function)
      Console.WriteLine("Outer has returned {0}", parent.Result)
   End Sub
End Module      
' The example displays the following output:
'       Outer task executing.
'       Nested task starting.
'       Detached task completing.
'       Outer has returned 42

Angefügte untergeordnete AufgabenAttached child tasks

Anders als getrennte untergeordnete Aufgaben werden angefügte untergeordnete Aufgaben eng mit dem übergeordneten Element synchronisiert.Unlike detached child tasks, attached child tasks are closely synchronized with the parent. Sie können die getrennte untergeordnete Aufgabe im vorherigen Beispiel in eine angefügte untergeordnete Aufgabe ändern, indem Sie wie im folgenden Beispiel gezeigt die TaskCreationOptions.AttachedToParent-Option in der Aufgabenerstellungsanweisung verwenden.You can change the detached child task in the previous example to an attached child task by using the TaskCreationOptions.AttachedToParent option in the task creation statement, as shown in the following example. In diesem Code schließt die angefügte untergeordnete Aufgabe vor ihrem übergeordneten Element ab.In this code, the attached child task completes before its parent. Deshalb ist das Ergebnis im Beispiel bei jedem Ausführen des Codes gleich.As a result, the output from the example is the same each time you run the code.

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

public class Example
{
   public static void Main()
   {
      var parent = Task.Factory.StartNew(() => {
            Console.WriteLine("Parent task executing.");
            var child = Task.Factory.StartNew(() => {
                  Console.WriteLine("Attached child starting.");
                  Thread.SpinWait(5000000);
                  Console.WriteLine("Attached child completing.");
            }, TaskCreationOptions.AttachedToParent);
      });
      parent.Wait();
      Console.WriteLine("Parent has completed.");
   }
}
// The example displays the following output:
//       Parent task executing.
//       Attached child starting.
//       Attached child completing.
//       Parent has completed.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task.Factory.StartNew(Sub()
                                            Console.WriteLine("Parent task executing")
                                            Dim child = Task.Factory.StartNew(Sub()
                                                                                 Console.WriteLine("Attached child starting.")
                                                                                 Thread.SpinWait(5000000)
                                                                                 Console.WriteLine("Attached child completing.")
                                                                              End Sub, TaskCreationOptions.AttachedToParent)
                                         End Sub)
      parent.Wait()
      Console.WriteLine("Parent has completed.")
   End Sub
End Module
' The example displays the following output:
'       Parent task executing.
'       Attached child starting.
'       Attached child completing.
'       Parent has completed.

Mithilfe von angefügten untergeordneten Aufgaben können Sie synchronisierte Diagramme von asynchronen Vorgängen erstellen.You can use attached child tasks to create tightly synchronized graphs of asynchronous operations.

Die untergeordnete Aufgabe kann jedoch nur dann an die übergeordnete Aufgabe angefügt werden, wenn die übergeordnete Aufgabe das Anfügen von untergeordneten Aufgaben nicht verhindert.However, a child task can attach to its parent only if its parent does not prohibit attached child tasks. Übergeordnete Aufgaben können explizit verhindern, dass untergeordnete Aufgaben angefügt werden, indem die TaskCreationOptions.DenyChildAttach-Option des Klassenkonstruktors der übergeordneten Aufgabe oder die TaskFactory.StartNew-Methode angegeben wird.Parent tasks can explicitly prevent child tasks from attaching to them by specifying the TaskCreationOptions.DenyChildAttach option in the parent task's class constructor or the TaskFactory.StartNew method. Übergeordnete Aufgaben verhindern das Anfügen von untergeordneten Aufgaben explizit, wenn sie mithilfe des Aufrufs der Task.Run-Methode erstellt werden.Parent tasks implicitly prevent child tasks from attaching to them if they are created by calling the Task.Run method. Dies wird anhand des folgenden Beispiels veranschaulicht.The following example illustrates this. Es ist weitestgehend mit dem vorherigen Beispiel identisch, allerdings wurde die übergeordnete Aufgabe hier mit der Task.Run(Action)-Methode und nicht mit der TaskFactory.StartNew(Action)-Methode erstellt.It is identical to the previous example, except that the parent task is created by calling the Task.Run(Action) method rather than the TaskFactory.StartNew(Action) method. Da die untergeordnete Aufgabe nicht an die übergeordnete Aufgabe angefügt werden kann, ist die Ausgabe dieses Beispiels nicht vorhersehbar.Because the child task is not able to attach to its parent, the output from the example is unpredictable. Da die standardmäßigen Aufgabenerstellungsoptionen für Task.Run-Überladungen TaskCreationOptions.DenyChildAttach einschließen, entspricht dieses Beispiel funktional dem ersten Beispiel im Abschnitt „Getrennte untergeordnete Aufgaben“.Because the default task creation options for the Task.Run overloads include TaskCreationOptions.DenyChildAttach, this example is functionally equivalent to the first example in the "Detached child tasks" section.

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

public class Example
{
   public static void Main()
   {
      var parent = Task.Run(() => {
            Console.WriteLine("Parent task executing.");
            var child = Task.Factory.StartNew(() => {
                  Console.WriteLine("Child starting.");
                  Thread.SpinWait(5000000);
                  Console.WriteLine("Child completing.");
            }, TaskCreationOptions.AttachedToParent);
      });
      parent.Wait();
      Console.WriteLine("Parent has completed.");
   }
}
// The example displays output like the following:
//       Parent task executing
//       Parent has completed.
//       Attached child starting.
Imports System.Threading
Imports System.Threading.Tasks

Module Example
   Public Sub Main()
      Dim parent = Task.Run(Sub()
                               Console.WriteLine("Parent task executing")
                               Dim child = Task.Factory.StartNew(Sub()
                                              Console.WriteLine("Attached child starting.")
                                              Thread.SpinWait(5000000)
                                              Console.WriteLine("Attached child completing.")
                                           End Sub, TaskCreationOptions.AttachedToParent)
                            End Sub)
      parent.Wait()
      Console.WriteLine("Parent has completed.")
   End Sub
End Module
' The example displays output like the following:
'       Parent task executing
'       Parent has completed.
'       Attached child starting.

Ausnahmen in untergeordneten AufgabenExceptions in child tasks

Wenn eine getrennte untergeordnete Aufgabe eine Ausnahme auslöst, muss diese direkt in der übergeordneten Aufgabe beachtet oder behandelt werden, ebenso wie dies bei nicht geschachtelten Aufgaben der Fall ist.If a detached child task throws an exception, that exception must be observed or handled directly in the parent task just as with any non-nested task. Wenn eine angefügte untergeordnete Aufgabe eine Ausnahme auslöst, wird die Ausnahme automatisch an die übergeordnete Aufgabe weitergeleitet und an den Thread zurückgesendet, der wartet oder auf die Task<TResult>.Result-Eigenschaft der Aufgabe zugreifen möchte.If an attached child task throws an exception, the exception is automatically propagated to the parent task and back to the thread that waits or tries to access the task's Task<TResult>.Result property. Daher können durch Verwendung angefügter untergeordneter Aufgaben alle Ausnahmen an nur einem Punkt, beim Aufruf von Task.Wait im aufrufenden Thread, verarbeitet werden.Therefore, by using attached child tasks, you can handle all exceptions at just one point in the call to Task.Wait on the calling thread. Weitere Informationen finden Sie unter Ausnahmebehandlung (Task Parallel Library).For more information, see Exception Handling.

Abbruch und untergeordnete AufgabenCancellation and child tasks

Das Abbrechen einer Aufgabe ist kooperativ.Task cancellation is cooperative. Um "abbrechbar" zu sein, muss jede angefügte oder getrennte untergeordnete Aufgabe den Status des Abbruchtokens überwachen.That is, to be cancelable, every attached or detached child task must monitor the status of the cancellation token. Wenn Sie mit einer Abbruchanforderung ein übergeordnetes Element und alle untergeordneten Elemente abbrechen möchten, übergeben Sie das gleiche Token als Argument an alle Aufgaben und stellen in jeder Aufgabe die Logik zum Reagieren auf die Anforderung in bereit.If you want to cancel a parent and all its children by using one cancellation request, you pass the same token as an argument to all tasks and provide in each task the logic to respond to the request in each task. Weitere Informationen finden Sie unter Aufgabenabbruch und Vorgehensweise: Abbrechen einer Aufgabe und ihrer untergeordneten Elemente.For more information, see Task Cancellation and How to: Cancel a Task and Its Children.

Abbruch eines übergeordneten ElementsWhen the parent cancels

Wenn eine übergeordnete Aufgabe abgebrochen wird, bevor eine untergeordnete Aufgabe gestartet wurde, wird die untergeordnete Aufgabe nie gestartet.If a parent cancels itself before its child task is started, the child never starts. Wenn ein übergeordnetes Element nach dem Start einer untergeordneten Aufgabe abgebrochen wird, wird die untergeordnete bis zum Abschluss ausgeführt, sofern keine eigene Abbruchlogik vorhanden ist.If a parent cancels itself after its child task has already started, the child runs to completion unless it has its own cancellation logic. Weitere Informationen finden Sie unter Aufgabenabbruch.For more information, see Task Cancellation.

Abbruch einer getrennten untergeordneten AufgabeWhen a detached child task cancels

Wenn eine getrennte untergeordnete Aufgabe mit dem gleichen Token abgebrochen wird, das an die übergeordnete Aufgabe übergeben wurde, und das übergeordnete Element nicht auf die untergeordnete Aufgabe wartet, wird keine Ausnahme weitergeleitet, da die Ausnahme als Kooperationsabbruch ohne Auswirkungen behandelt wird.If a detached child task cancels itself by using the same token that was passed to the parent, and the parent does not wait for the child task, no exception is propagated, because the exception is treated as benign cooperation cancellation. Dieses Verhalten stimmt mit dem beliebiger Aufgaben der obersten Ebene überein.This behavior is the same as that of any top-level task.

Abbruch einer angefügten untergeordneten AufgabeWhen an attached child task cancels

Wenn eine angefügte untergeordnete Aufgabe mit dem gleichen Token abgebrochen wird, das an die übergeordnete Aufgabe übergeben wurde, wird eine TaskCanceledException an den Verbindungsthread in einer AggregateException weitergeleitet.When an attached child task cancels itself by using the same token that was passed to its parent task, a TaskCanceledException is propagated to the joining thread inside an AggregateException. Sie müssen auf die übergeordnete Aufgabe warten, damit Sie die Ausnahmen ohne Auswirkung zusammen mit allen einen Fehler auslösenden Ausnahmen behandeln können, die durch ein Diagramm angefügter untergeordneter Aufgaben nach oben weitergeleitet werden.You must wait for the parent task so that you can handle all benign exceptions in addition to all faulting exceptions that are propagated up through a graph of attached child tasks.

Weitere Informationen finden Sie unter Ausnahmebehandlung (Task Parallel Library).For more information, see Exception Handling.

Verhindern, dass sich eine untergeordnete Aufgabe mit ihrem übergeordneten Element verbindetPreventing a child task from attaching to its parent

Ein Ausnahmefehler, der durch eine untergeordnete Aufgabe ausgelöst wird, wird an die übergeordnete Aufgabe weitergegeben.An unhandled exception that is thrown by a child task is propagated to the parent task. Sie können dieses Verhalten nutzen, um alle Ausnahmen von untergeordneten Aufgaben einer Stammaufgabe zu beobachten anstatt eine Aufgabenstruktur zu durchlaufen.You can use this behavior to observe all child task exceptions from one root task instead of traversing a tree of tasks. Die Ausnahmeweitergabe kann jedoch problematisch werden, wenn eine übergeordnete Aufgabe einen Anhang aus einem anderen Code erwartet.However, exception propagation can be problematic when a parent task does not expect attachment from other code. Betrachten Sie beispielsweise eine Anwendung, die eine Bibliothekskomponente eines Drittanbietern aus einem Task-Objekt aufruft.For example, consider an app that calls a third-party library component from a Task object. Wenn die Bibliothekskomponente von Drittanbietern auch ein Task-Objekt erstellt und TaskCreationOptions.AttachedToParent angibt, um es zur übergeordneten Aufgabe anzufügen, werden alle Ausnahmefehler, die in der untergeordneten Aufgabe auftreten, an das übergeordnete Element weitergegeben.If the third-party library component also creates a Task object and specifies TaskCreationOptions.AttachedToParent to attach it to the parent task, any unhandled exceptions that occur in the child task propagate to the parent. Dies kann zu unerwartetem Verhalten in der Haupt-App führen.This could lead to unexpected behavior in the main app.

Wenn eine untergeordnete Aufgabe nicht an die übergeordnete Aufgabe angefügt werden soll, geben Sie die TaskCreationOptions.DenyChildAttach-Option an, wenn Sie das übergeordnete Task oder das Task<TResult>-Objekt erstellen.To prevent a child task from attaching to its parent task, specify the TaskCreationOptions.DenyChildAttach option when you create the parent Task or Task<TResult> object. Wenn eine Aufgabe versucht, sich an ihre übergeordnete Aufgabe anzufügen und für die übergeordnete Aufgabe die TaskCreationOptions.DenyChildAttach-Option festgelegt wurde, kann die untergeordnete Aufgabe nicht an die übergeordnete Aufgabe angefügt werden und wird so ausgeführt, als sei die TaskCreationOptions.AttachedToParent-Option nicht festgelegt worden.When a task tries to attach to its parent and the parent specifies the TaskCreationOptions.DenyChildAttach option, the child task will not be able to attach to a parent and will execute just as if the TaskCreationOptions.AttachedToParent option was not specified.

Sie können auch verhindern, dass sich eine untergeordnete Aufgabe an das übergeordnete Element anfügt, wenn die untergeordnete Aufgabe nicht rechtzeitig beendet wird.You might also want to prevent a child task from attaching to its parent when the child task does not finish in a timely manner. Da eine übergeordnete Aufgabe nicht beendet wird, ehe alle untergeordneten Aufgaben abgeschlossen sind, kann eine untergeordnete Aufgabe mit langer Laufzeit zu einer Verschlechterung der Leistung der gesamten App führen.Because a parent task does not finish until all child tasks finish, a long-running child task can cause the overall app to perform poorly. Ein Beispiel zur Verbesserung der App-Leistung durch Verhindern des Anfügens einer Aufgabe an das übergeordnete Element finden Sie unter Vorgehensweise: Verhindern des Anfügens einer untergeordneten Aufgabe an die übergeordnete Aufgabe.For an example that shows how to improve app performance by preventing a task from attaching to its parent task, see How to: Prevent a Child Task from Attaching to its Parent.

Siehe auchSee also