Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
En underordnad uppgift (eller kapslad uppgift) är en System.Threading.Tasks.Task instans som skapas i användardelegatet för en annan uppgift, som kallas för den överordnade uppgiften. En underordnad uppgift kan antingen vara frånkopplad eller ansluten. En frånkopplad barnuppgift är en uppgift som körs oberoende av huvuduppgiften. 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 barnuppgifter.
| Kategori | Oberoende barnuppgifter | Bifogade underordnade uppgifter |
|---|---|---|
| Förälder väntar på att barntjänster ska slutföras. | Nej | Ja |
| Förälder hanterar undantag som genereras av underordnade uppgifter. | Nej | Ja |
| Status för förälder beror på status för barn. | 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.
Oberoende barnuppgifter
Även om en underordnad uppgift skapas av en överordnad uppgift är den som standard oberoende av den överordnade uppgiften. 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 uppgiften och de underordnade uppgifterna körs oberoende av varandra. Barnuppgiften ä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 Example4
{
public static void Main()
{
Task parent = Task.Factory.StartNew(() =>
{
Console.WriteLine("Outer task executing.");
Task 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 underuppgiften representeras av ett Task<TResult> objekt istället för ett Task objekt, kan du säkerställa att huvuduppgiften väntar på att underuppgiften ska avslutas genom att komma åt Task<TResult>.Result egenskapen för underobjektet, även om det är en självständig underuppgift. Egenskapen Result blockerar tills dess uppgift är slutförd, vilket visas i följande exempel.
using System;
using System.Threading;
using System.Threading.Tasks;
class Example3
{
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 {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
Bifogade underordnade uppgifter
Till skillnad från frånkopplade barnuppgifter synkroniseras anslutna barnuppgifter nära med den överordnade. Du kan ändra den frånkopplade underordnade aktiviteten i föregående exempel till en bifogad underordnad aktivitet genom att använda alternativet TaskCreationOptions.AttachedToParent i instruktionen för att skapa aktiviteten, som visas i följande exempel. I den här koden slutförs den bifogade underordnade uppgiften före den överordnade uppgiften. 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 bifogade underordnade uppgifter för att skapa tätsynkroniserade grafer av asynkrona operationer.
En underordnad uppgift kan dock endast kopplas till sin överordnade uppgift om den överordnade uppgiften inte förbjuder kopplade underordnade uppgifter. Överordnade uppgifter kan explicit förhindra att underordnade uppgifter associeras med dem genom att ange TaskCreationOptions.DenyChildAttach-alternativet i den överordnade uppgiftens klasskonstruktor eller den 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 uppgiften inte kan kopplas till den överordnade uppgiften är utdata från exemplet oförutsägbart. Eftersom standardalternativen Task.Run för att skapa aktiviteter för överladdningar innefattar TaskCreationOptions.DenyChildAttach, är det här exemplet funktionellt likvärdigt med det första exemplet i avsnittet "Frånkopplade underordnade aktiviteter".
using System;
using System.Threading;
using System.Threading.Tasks;
public class Example2
{
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 barntjänster
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. Därför kan du genom att använda bifogade barnuppgifter hantera alla undantag vid endast en punkt i anropet till Task.Wait på den anropande tråden. Mer information finns i Undantagshantering.
Annullering och underordnade uppgifter
Aktivitetsavbokningen är samarbetsinriktad. För att vara möjlig att avbryta måste varje bifogad eller frånkopplad underordnad uppgift övervaka statusen för avbrytningstoken. Om du vill avbryta en förälder och alla dess barn med hjälp av en enda begäran om annullering skickar du samma token som ett argument till alla uppgifter och anger logiken i varje uppgift för hur de ska svara på begäran. För mer information, se Aktivitetens avbrytande och Så här gör du: Avbryt en aktivitet och dess underordnade.
När föräldern avbryter
Om en överordnad avbryter sig själv innan den underordnade aktiviteten startas startar aldrig barnet. Om en förälder avbryter sig själv efter att dess barnuppgift redan har börjat, körs barnuppgiften till slut om den inte har sin egen annulleringslogik. Mer information finns i Aktivitetsavbokning.
När en fristående underordnad uppgift 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 barnuppgift sprids till föräldernuppgiften. 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 specificerar TaskCreationOptions.AttachedToParent för att koppla det till den överordnade uppgiften, sprids eventuella ohanterade undantag som inträffar i den underordnade uppgiften till den överordnade uppgiften. 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, kommer den underordnade aktiviteten inte att kunna 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 på hur du kan förbättra appens prestanda genom att förhindra en uppgift från att ansluta till sin föräldrauppgift finns i Så här förhindrar du att en underuppgift ansluter till sin föräldrauppgift.