Share via


Geschachtelte Aufgaben und untergeordnete Aufgaben

Eine geschachtelte Aufgabe ist eine Task-Instanz, die im Benutzerdelegaten einer anderen Aufgabe erstellt wird. Eine untergeordnete Aufgabe ist eine geschachtelte Aufgabe, die mit der AttachedToParent-Option erstellt wird. Eine Aufgabe kann beliebig viele untergeordnete und/oder geschachtelte Aufgaben erstellen. Die Anzahl wird lediglich durch die Systemressourcen beschränkt. Im folgenden Beispiel wird eine übergeordnete Aufgabe dargestellt, die eine einfache geschachtelte Aufgabe erstellt.

Shared Sub SimpleNestedTask()
    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

' Sample output:
'   Outer task executing.
'   Nested task starting.
'   Outer task has completed.
'   Nested task completing.
static void SimpleNestedTask()
{
    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.");
}

/* Sample output:
        Outer task executing.
        Nested task starting.
        Outer has completed.
        Nested task completing.
 */

Angefügte untergeordnete Aufgaben und getrennte geschachtelte Aufgaben

Der wichtigste Unterschied zwischen untergeordneten Aufgaben und geschachtelten Aufgaben besteht darin, dass die geschachtelten Aufgaben von der übergeordneten oder äußeren Aufgabe weitgehend unabhängig sind, wohingegen angefügte untergeordnete Aufgaben mit dem übergeordneten Element synchronisiert werden. Ändern Sie die Aufgabenerstellungsanweisung so, dass diese die AttachedToParent-Option verwendet, wie im folgenden Beispiel gezeigt:

Dim child = Task.Factory.StartNew(Sub()
                                      Console.WriteLine("Attached child starting.")
                                      Thread.SpinWait(5000000)
                                      Console.WriteLine("Attached child completing.")
                                  End Sub, TaskCreationOptions.AttachedToParent)
var child = Task.Factory.StartNew((t) =>
{

    Console.WriteLine("Attached child starting.");
    Thread.SpinWait(5000000);
    Console.WriteLine("Attached child completing.");
},                TaskCreationOptions.AttachedToParent);

Hierdurch wird die folgende Ausgabe erzeugt:

' Parent task executing.
' Attached child starting.
' Attached child completing.
' Parent has completed.
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. In den meisten Szenarios empfiehlt sich jedoch die Verwendung von geschachtelten Aufgaben, da die Beziehungen zu anderen Aufgaben weniger komplex sind. Aus diesem Grund werden in anderen Aufgaben erstellte Aufgaben standardmäßig geschachtelt. Um untergeordnete Aufgaben zu erstellen, müssen Sie die AttachedToParent-Option explizit angeben.

In der folgenden Tabelle sind die grundlegenden Unterschiede zwischen den zwei Arten von untergeordneten Aufgaben aufgeführt.

Kategorie

Geschachtelte Aufgaben

Angefügte untergeordnete Aufgaben

Die äußere (übergeordnete) Aufgabe wartet, bis die innere Aufgaben abgeschlossen ist.

Nein

Ja

Das übergeordnete Element gibt von untergeordneten Elementen (inneren Aufgaben) ausgelöste Ausnahmen weiter.

Nein

Ja

Der Status der übergeordnet (äußeren) Aufgabe ist abhängig vom Status der untergeordneten (inneren) Aufgabe.

Nein

Ja

In getrennten Szenarios, in denen die geschachtelte Aufgabe eine Task<TResult> ist, können Sie erzwingen, dass das übergeordnete Element auf ein untergeordnetes Element wartet, indem Sie auf die Result-Eigenschaft der geschachtelten Aufgabe zugreifen. Die Result-Eigenschaft wird gesperrt, bis die zugehörige Aufgabe abgeschlossen ist.

Shared Sub WaitForSimpleNestedTask()
    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
'Sample output:
' Outer task executing.
' Nested task starting.
' Detached task completing.
' Outer has returned 42
static void WaitForSimpleNestedTask()
{
    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);
}

/* Sample output:
    Outer task executing.
    Nested task starting.
    Nested task completing.
    Outer has returned 42.
 */

Ausnahmen bei geschachtelten und untergeordneten Aufgaben

Wenn eine geschachtelte Aufgabe eine Ausnahme auslöst, muss diese in der äußeren Aufgabe direkt beachtet oder behandelt werden, ebenso wie dies bei nicht geschachtelten Aufgaben der Fall ist. 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 Result-Eigenschaft der Aufgabe zugreifen möchte. Daher können durch Verwendung angefügter untergeordneter Aufgaben alle Ausnahmen an nur einem Punkt, nämlich beim Aufruf von Wait im aufrufenden Thread, verarbeitet werden. Weitere Informationen finden Sie unter Ausnahmebehandlung (Task Parallel Library).

Abbruch und untergeordnete Aufgaben

Beachten Sie, dass der Aufgabenabbruch kooperativ erfolgt. Um "abbrechbar" zu sein, muss daher jede angefügte oder getrennte untergeordnete Aufgabe den Status des Abbruchtokens überwachen. Wenn Sie mit nur einer Abbruchanforderung ein übergeordnetes Element und alle untergeordneten Elemente abbrechen möchten, übergeben Sie das gleiche Token als Argument an alle Aufgaben, und stellen Sie in jeder Aufgabe die zum Reagieren auf die Anforderung erforderliche Logik bereit. Weitere Informationen finden Sie unter Aufgabenabbruch und Gewusst wie: Abbrechen einer Aufgabe und ihrer untergeordneten Elemente.

Abbruch des übergeordneten Elements

Wenn ein übergeordnetes Element abgebrochen wird, bevor ein untergeordnetes Element gestartet wurde, wird die untergeordnete (geschachtelte) Aufgabe nicht gestartet. Wenn ein übergeordnetes Element nach dem Start einer untergeordneten oder geschachtelten Aufgabe abgebrochen wird, wird die geschachtelte (untergeordnete) Aufgabe abgeschlossen, sofern sie nicht über eigene Abbruchlogik verfügt. Weitere Informationen finden Sie unter Aufgabenabbruch.

Abbruch einer geschachtelten Aufgabe

Wenn eine getrennte untergeordnete Aufgabe mit dem gleichen Token abgebrochen wird, das an die Aufgabe übergeben wurde, und das übergeordnete Element nicht auf das untergeordnete Element wartet, wird keine Ausnahme weitergeleitet, da die Ausnahme als Kooperationsabbruch ohne Auswirkungen behandelt wird. Dieses Verhalten stimmt mit dem beliebiger Aufgaben der obersten Ebene überein.

Abbruch einer untergeordneten Aufgabe

Wenn eine angefügte untergeordnete Aufgabe mit dem gleichen Token abgebrochen wird, das an die Aufgabe übergeben wurde, wird eine TaskCanceledExceptionan den Verbindungsthread in einer AggregateException weitergeleitet. Es ist wichtig, dass auf die übergeordnete Aufgabe gewartet wird, 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.

Weitere Informationen finden Sie unter Ausnahmebehandlung (Task Parallel Library).

Siehe auch

Konzepte

Parallele Programmierung in .NET Framework

Datenparallelität (Task Parallel Library)