Delen via


Gekoppelde en losgekoppelde onderliggende taken

Een onderliggende taak (of geneste taak) is een System.Threading.Tasks.Task exemplaar dat wordt gemaakt in de gebruikersdelegatie van een andere taak, die de bovenliggende taak wordt genoemd. Een onderliggende taak kan worden losgekoppeld of gekoppeld. Een losgekoppelde onderliggende taak is een taak die onafhankelijk van het bovenliggende item wordt uitgevoerd. Een gekoppelde onderliggende taak is een geneste taak die wordt gemaakt met de optie waarvan het TaskCreationOptions.AttachedToParent bovenliggende item niet expliciet of standaard verhindert dat deze wordt gekoppeld. Een taak kan een willekeurig aantal gekoppelde en losgekoppelde onderliggende taken maken, alleen beperkt door systeembronnen.

De volgende tabel bevat de basisverschillen tussen de twee soorten onderliggende taken.

Categorie Losgekoppelde onderliggende taken Gekoppelde onderliggende taken
Bovenliggende taken worden voltooid. Nr. Ja
Bovenliggende items worden uitzonderingen doorgegeven die zijn gegenereerd door onderliggende taken. Nr. Ja
De status van ouder is afhankelijk van de status van het kind. Nr. Ja

In de meeste scenario's wordt u aangeraden losgekoppelde onderliggende taken te gebruiken, omdat hun relaties met andere taken minder complex zijn. Daarom worden taken die in bovenliggende taken zijn gemaakt standaard losgekoppeld en moet u expliciet de TaskCreationOptions.AttachedToParent optie opgeven om een gekoppelde onderliggende taak te maken.

Losgekoppelde onderliggende taken

Hoewel een onderliggende taak wordt gemaakt door een bovenliggende taak, is deze standaard onafhankelijk van de bovenliggende taak. In het volgende voorbeeld maakt een bovenliggende taak één eenvoudige onderliggende taak. Als u de voorbeeldcode meerdere keren uitvoert, ziet u mogelijk dat de uitvoer van het voorbeeld verschilt van de weergegeven code en dat de uitvoer kan veranderen telkens wanneer u de code uitvoert. Dit gebeurt omdat de bovenliggende en onderliggende taken onafhankelijk van elkaar worden uitgevoerd; het onderliggende item is een losgekoppelde taak. In het voorbeeld wordt alleen gewacht totdat de bovenliggende taak is voltooid en de onderliggende taak mogelijk niet wordt uitgevoerd of voltooid voordat de console-app wordt beëindigd.

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.

Als de onderliggende taak wordt vertegenwoordigd door een Task<TResult> object in plaats van een Task object, kunt u ervoor zorgen dat de bovenliggende taak wacht tot het onderliggende item is voltooid door toegang te krijgen tot de Task<TResult>.Result eigenschap van het onderliggende object, zelfs als het een losgekoppelde onderliggende taak is. De Result eigenschap wordt geblokkeerd totdat de taak is voltooid, zoals in het volgende voorbeeld wordt weergegeven.

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.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

Gekoppelde onderliggende taken

In tegenstelling tot losgekoppelde onderliggende taken worden gekoppelde onderliggende taken nauw gesynchroniseerd met het bovenliggende item. U kunt de losgekoppelde onderliggende taak in het vorige voorbeeld wijzigen in een gekoppelde onderliggende taak met behulp van de TaskCreationOptions.AttachedToParent optie in de instructie voor het maken van taken, zoals wordt weergegeven in het volgende voorbeeld. In deze code wordt de gekoppelde onderliggende taak voltooid vóór het bovenliggende item. Als gevolg hiervan is de uitvoer uit het voorbeeld steeds hetzelfde wanneer u de code uitvoert.

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.

U kunt gekoppelde onderliggende taken gebruiken om nauw gesynchroniseerde grafieken van asynchrone bewerkingen te maken.

Een onderliggende taak kan echter alleen aan de bovenliggende taak worden gekoppeld als het bovenliggende item geen gekoppelde onderliggende taken verbiedt. Bovenliggende taken kunnen expliciet voorkomen dat onderliggende taken aan deze taken worden gekoppeld door de TaskCreationOptions.DenyChildAttach optie op te geven in de klasseconstructor van de bovenliggende taak of de TaskFactory.StartNew methode. Bovenliggende taken verhinderen impliciet dat onderliggende taken aan hen worden gekoppeld als ze worden gemaakt door de methode aan te Task.Run roepen. In het volgende voorbeeld ziet u dit. Het is identiek aan het vorige voorbeeld, behalve dat de bovenliggende taak wordt gemaakt door de Task.Run(Action) methode aan te roepen in plaats van de TaskFactory.StartNew(Action) methode. Omdat de onderliggende taak niet aan het bovenliggende item kan worden gekoppeld, is de uitvoer van het voorbeeld onvoorspelbaar. Omdat de standaardopties voor het maken van taken voor de Task.Run overbelastingen zijn, is TaskCreationOptions.DenyChildAttachdit voorbeeld functioneel gelijk aan het eerste voorbeeld in de sectie 'Losgekoppelde onderliggende taken'.

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("Attached child starting.");
                  Thread.SpinWait(5000000);
                  Console.WriteLine("Attached 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.

Uitzonderingen in onderliggende taken

Als een losgekoppelde onderliggende taak een uitzondering genereert, moet die uitzondering direct in de bovenliggende taak worden waargenomen of afgehandeld, net als bij een niet-geneste taak. Als een gekoppelde onderliggende taak een uitzondering genereert, wordt de uitzondering automatisch doorgegeven aan de bovenliggende taak en terug naar de thread die wacht of probeert toegang te krijgen tot de eigenschap van Task<TResult>.Result de taak. Daarom kunt u met behulp van gekoppelde onderliggende taken alle uitzonderingen op slechts één punt in de aanroep afhandelen op de aanroepende Task.Wait thread. Zie Uitzonderingsafhandeling voor meer informatie.

Annulerings- en onderliggende taken

Taakannulering is coöperatief. Dat wil zeggen dat elke gekoppelde of losgekoppelde onderliggende taak de status van het annuleringstoken moet controleren. Als u een bovenliggende en alle onderliggende items wilt annuleren met behulp van één annuleringsaanvraag, geeft u hetzelfde token als een argument door aan alle taken en geeft u in elke taak de logica op om te reageren op de aanvraag in elke taak. Zie Taakannulering en Procedures voor meer informatie: Een taak en de onderliggende items annuleren.

Wanneer het bovenliggende item wordt geannuleerd

Als een ouder zichzelf annuleert voordat de onderliggende taak wordt gestart, wordt het kind nooit gestart. Als een bovenliggend item zichzelf annuleert nadat de onderliggende taak al is gestart, wordt het onderliggende item uitgevoerd tot voltooiing, tenzij het een eigen annuleringslogica heeft. Zie Taakannulering voor meer informatie.

Wanneer een losgekoppelde onderliggende taak wordt geannuleerd

Als een losgekoppelde onderliggende taak zichzelf annuleert met hetzelfde token dat is doorgegeven aan het bovenliggende item en het bovenliggende item niet wacht op de onderliggende taak, wordt er geen uitzondering doorgegeven, omdat de uitzondering wordt beschouwd als goedaardige annulering van samenwerking. Dit gedrag is hetzelfde als die van een taak op het hoogste niveau.

Wanneer een gekoppelde onderliggende taak wordt geannuleerd

Wanneer een gekoppelde onderliggende taak zichzelf annuleert met hetzelfde token dat is doorgegeven aan de bovenliggende taak, wordt een TaskCanceledException doorgegeven aan de samenvoegingsthread in een AggregateException. U moet wachten op de bovenliggende taak, zodat u alle goedaardige uitzonderingen kunt afhandelen, naast alle foutieve uitzonderingen die worden doorgegeven via een grafiek met gekoppelde onderliggende taken.

Zie Uitzonderingsafhandeling voor meer informatie.

Voorkomen dat een onderliggende taak wordt gekoppeld aan het bovenliggende item

Een niet-verwerkte uitzondering die wordt gegenereerd door een onderliggende taak, wordt doorgegeven aan de bovenliggende taak. U kunt dit gedrag gebruiken om alle onderliggende taakuitzondering van één hoofdtaak te observeren in plaats van een structuur met taken te doorlopen. Uitzonderingsdoorgifte kan echter problematisch zijn wanneer een bovenliggende taak geen bijlage van andere code verwacht. Denk bijvoorbeeld aan een app die een bibliotheekonderdeel van derden aanroept vanuit een Task object. Als het bibliotheekonderdeel van derden ook een Task object maakt en aangeeft TaskCreationOptions.AttachedToParent dat het aan de bovenliggende taak moet worden gekoppeld, worden eventuele niet-verwerkte uitzonderingen die voorkomen in de onderliggende taak doorgegeven aan het bovenliggende object. Dit kan leiden tot onverwacht gedrag in de hoofd-app.

Als u wilt voorkomen dat een onderliggende taak aan de bovenliggende taak wordt gekoppeld, geeft u de TaskCreationOptions.DenyChildAttach optie op wanneer u het bovenliggende Task of Task<TResult> object maakt. Wanneer een taak probeert te koppelen aan het bovenliggende item en het bovenliggende item de TaskCreationOptions.DenyChildAttach optie opgeeft, kan de onderliggende taak niet aan een bovenliggende taak worden gekoppeld en wordt deze uitgevoerd alsof de TaskCreationOptions.AttachedToParent optie niet is opgegeven.

Mogelijk wilt u ook voorkomen dat een onderliggende taak aan het bovenliggende item wordt gekoppeld wanneer de onderliggende taak niet tijdig eindigt. Omdat een bovenliggende taak pas wordt voltooid als alle onderliggende taken zijn voltooid, kan een langlopende onderliggende taak ertoe leiden dat de algehele app slecht presteert. Voor een voorbeeld waarin wordt getoond hoe u de prestaties van apps kunt verbeteren door te voorkomen dat een taak wordt gekoppeld aan de bovenliggende taak, raadpleegt u Procedure: Voorkomen dat een onderliggende taak wordt gekoppeld aan het bovenliggende item.

Zie ook