Dela via


Kopplade och frånkopplade underordnade uppgifter

En underordnad uppgift (eller kapslad uppgift) är en System.Threading.Tasks.Task instans som skapas i användardelegaten för en annan uppgift, som kallas för den överordnade aktiviteten. En underordnad uppgift kan kopplas från eller kopplas. En frånkopplad underordnad aktivitet är en aktivitet som körs oberoende av dess överordnade uppgift. En bifogad underordnad uppgift är en kapslad uppgift som skapas med alternativet TaskCreationOptions.AttachedToParent vars överordnade uppgift inte uttryckligen eller som standard förhindrar att den kopplas. En uppgift kan skapa valfritt antal anslutna och frånkopplade underordnade aktiviteter, som endast begränsas av systemresurser.

I följande tabell visas de grundläggande skillnaderna mellan de två typerna av underordnade uppgifter.

Kategori Frånkopplade underordnade uppgifter Anslutna underordnade uppgifter
Överordnad väntar på att underordnade uppgifter ska slutföras. Nej Ja
Överordnad sprider undantag som genereras av underordnade uppgifter. Nej Ja
Status för överordnad beror på status för underordnad. Nej Ja

I de flesta scenarier rekommenderar vi att du använder fristående underordnade uppgifter, eftersom deras relationer med andra uppgifter är mindre komplexa. Därför kopplas aktiviteter som skapats i överordnade aktiviteter från som standard, och du måste uttryckligen ange alternativet för att skapa en bifogad underordnad TaskCreationOptions.AttachedToParent aktivitet.

Frånkopplade underordnade uppgifter

Även om en underordnad aktivitet skapas av en överordnad aktivitet är den som standard oberoende av den överordnade aktiviteten. I följande exempel skapar en överordnad uppgift en enkel underordnad uppgift. Om du kör exempelkoden flera gånger kan du märka att utdata från exemplet skiljer sig från det som visas, och även att utdata kan ändras varje gång du kör koden. Detta beror på att den överordnade aktiviteten och underordnade uppgifter körs oberoende av varandra. underordnad är en frånkopplad uppgift. Exemplet väntar bara på att den överordnade aktiviteten ska slutföras och den underordnade aktiviteten kanske inte körs eller slutförs innan konsolappen avslutas.

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.

Om den underordnade aktiviteten representeras av ett Task<TResult> objekt i stället för ett Task objekt kan du se till att den överordnade aktiviteten väntar på att den underordnade aktiviteten ska slutföras genom att komma åt Task<TResult>.Result egenskapen för det underordnade objektet även om det är en frånkopplad underordnad uppgift. Egenskapen Result blockeras tills dess uppgift har slutförts, vilket visas i följande exempel.

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

Anslutna underordnade uppgifter

Till skillnad från frånkopplade underordnade aktiviteter synkroniseras kopplade underordnade aktiviteter nära med den överordnade. Du kan ändra den kopplade underordnade aktiviteten i föregående exempel till en bifogad underordnad TaskCreationOptions.AttachedToParent aktivitet med hjälp av alternativet i instruktionen för att skapa aktiviteter, som du ser i följande exempel. I den här koden slutförs den kopplade underordnade aktiviteten före den överordnade aktiviteten. Därför är utdata från exemplet desamma varje gång du kör koden.

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.

Du kan använda anslutna underordnade uppgifter för att skapa nära synkroniserade grafer med asynkrona åtgärder.

En underordnad aktivitet kan dock endast kopplas till den överordnade aktiviteten om den överordnade aktiviteten inte förbjuder anslutna underordnade aktiviteter. Överordnade uppgifter kan uttryckligen förhindra att underordnade aktiviteter kopplas till dem genom att TaskCreationOptions.DenyChildAttach ange alternativet i den överordnade aktivitetens klasskonstruktor eller TaskFactory.StartNew -metoden. Överordnade aktiviteter förhindrar implicit att underordnade aktiviteter kopplas till dem om de skapas genom att anropa Task.Run metoden. I följande exempel visas detta. Den är identisk med föregående exempel, förutom att den överordnade aktiviteten skapas genom att anropa Task.Run(Action) metoden i stället för TaskFactory.StartNew(Action) metoden. Eftersom den underordnade aktiviteten inte kan kopplas till den överordnade aktiviteten är utdata från exemplet oförutsägbara. Eftersom standardalternativen Task.Run för att skapa aktiviteter för överlagringar är TaskCreationOptions.DenyChildAttachdet här exemplet funktionellt likvärdigt med det första exemplet i avsnittet "Koppla från underordnade aktiviteter".

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.

Undantag i underordnade uppgifter

Om en frånkopplad underordnad aktivitet utlöser ett undantag måste undantaget observeras eller hanteras direkt i den överordnade aktiviteten precis som med alla icke-kapslade aktiviteter. Om en bifogad underordnad aktivitet genererar ett undantag sprids undantaget automatiskt till den överordnade aktiviteten och tillbaka till tråden som väntar eller försöker komma åt aktivitetens Task<TResult>.Result egenskap. Genom att använda anslutna underordnade uppgifter kan du därför hantera alla undantag vid bara en punkt i anropet till Task.Wait i den anropande tråden. Mer information finns i Undantagshantering.

Annullering och underordnade uppgifter

Aktivitetsavbokningen är samarbetsinriktad. För att kunna avbrytas måste varje bifogad eller frånkopplad underordnad uppgift övervaka statusen för annulleringstoken. Om du vill avbryta en överordnad och alla underordnade objekt med hjälp av en begäran om annullering skickar du samma token som ett argument till alla aktiviteter och anger logiken i varje uppgift för att svara på begäran i varje uppgift. Mer information finns i Annullering av aktiviteter och Så här avbryter du en aktivitet och dess underordnade aktiviteter.

När den överordnade avbryter

Om en överordnad avbryter sig själv innan den underordnade aktiviteten startas startar aldrig barnet. Om en överordnad avbryter sig själv efter att dess underordnade uppgift redan har startats, körs det underordnade objektet till slutförande om det inte har sin egen annulleringslogik. Mer information finns i Annullering av aktiviteter.

När en frånkopplad underordnad aktivitet avbryts

Om en frånkopplad underordnad aktivitet avbryter sig själv med samma token som skickades till den överordnade aktiviteten och den överordnade inte väntar på den underordnade aktiviteten sprids inget undantag, eftersom undantaget behandlas som avbrutet godartat samarbete. Det här beteendet är detsamma som för alla aktiviteter på den översta nivån.

När en bifogad underordnad aktivitet avbryts

När en bifogad underordnad uppgift avbryter sig själv med hjälp av samma token som skickades till den överordnade aktiviteten, sprids en TaskCanceledException till kopplingstråden i en AggregateException. Du måste vänta på den överordnade aktiviteten så att du kan hantera alla ogynnsamma undantag utöver alla felande undantag som sprids via ett diagram över kopplade underordnade aktiviteter.

Mer information finns i Undantagshantering.

Förhindra att en underordnad uppgift ansluter till sin överordnade uppgift

Ett ohanterat undantag som genereras av en underordnad uppgift sprids till den överordnade aktiviteten. Du kan använda det här beteendet för att observera alla underordnade aktivitetsfel från en rotaktivitet i stället för att gå igenom ett träd med aktiviteter. Undantagsspridning kan dock vara problematisk när en överordnad uppgift inte förväntar sig en bifogad fil från annan kod. Tänk dig till exempel en app som anropar en bibliotekskomponent från tredje part från ett Task objekt. Om bibliotekskomponenten från tredje part också skapar ett Task objekt och anger att den ska kopplas TaskCreationOptions.AttachedToParent till den överordnade aktiviteten sprids eventuella ohanterade undantag som inträffar i den underordnade aktiviteten till den överordnade aktiviteten. Detta kan leda till oväntat beteende i huvudappen.

Om du vill förhindra att en underordnad uppgift ansluter till den överordnade aktiviteten anger du alternativet TaskCreationOptions.DenyChildAttach när du skapar den överordnade Task aktiviteten eller Task<TResult> objektet. När en aktivitet försöker koppla till sin överordnade och den överordnade anger TaskCreationOptions.DenyChildAttach alternativet kan den underordnade aktiviteten inte kopplas till en överordnad och körs precis som om TaskCreationOptions.AttachedToParent alternativet inte angavs.

Du kanske också vill förhindra att en underordnad aktivitet ansluter till den överordnade aktiviteten när den underordnade aktiviteten inte slutförs i tid. Eftersom en överordnad aktivitet inte slutförs förrän alla underordnade aktiviteter har slutförts kan en långvarig underordnad aktivitet göra att den övergripande appen presterar dåligt. Ett exempel som visar hur du förbättrar appens prestanda genom att förhindra att en aktivitet ansluter till den överordnade aktiviteten finns i Så här förhindrar du att en underordnad aktivitet ansluter till den överordnade aktiviteten.

Se även