Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
podřízený úkol (nebo vnořený úkol) je instance System.Threading.Tasks.Task vytvořená v uživatelsky specifikovaném delegátu jiného úkolu, který se označuje jako nadřazený úkol. Podřízený úkol může být nezávislý nebo připojený. Nezávislý podřízený úkol je úloha, která se provádí nezávisle na nadřazené úloze. připojený podřízený úkol je vnořená úloha vytvořená s možností TaskCreationOptions.AttachedToParent, jejíž nadřazený úkol explicitně nebo defaultně nezakazuje její připojení. Úkol může vytvořit libovolný počet připojených a odpojených podřízených úkolů, omezený pouze systémovými prostředky.
Následující tabulka uvádí základní rozdíly mezi dvěma typy podřízených úkolů.
| Kategorie | Odpojené podřízené úkoly | Připojené podřízené úkoly |
|---|---|---|
| Rodič čeká na dokončení podřízených úkolů. | Ne | Ano |
| Rodiče šíří výjimky vyvolané podřízenými úlohami. | Ne | Ano |
| Stav rodiče závisí na stavu dítěte. | Ne | Ano |
Ve většině scénářů doporučujeme používat nezávislé podřízené úkoly, protože jejich vztahy s jinými úkoly jsou méně složité. Proto jsou úkoly vytvořené uvnitř nadřazených úkolů ve výchozím nastavení odpojené a je nutné explicitně zadat možnost TaskCreationOptions.AttachedToParent pro vytvoření připojeného podřízeného úkolu.
Odpojené podřízené úkoly
Přestože je podřízený úkol vytvořen nadřazeným úkolem, ve výchozím nastavení je nezávislý na nadřazené úloze. V následujícím příkladu vytvoří nadřazený úkol jeden jednoduchý podřízený úkol. Pokud vzorový kód spustíte několikrát, můžete si všimnout, že se výstup z příkladu liší od zobrazeného příkladu a také že se výstup může při každém spuštění kódu změnit. K tomu dochází, protože nadřazené a podřízené úkoly se provádějí nezávisle na sobě; podřízené úkoly jsou proto oddělené. Příklad čeká pouze na dokončení nadřazené úlohy a podřízená úloha se nemusí spustit ani dokončit před ukončením konzolové aplikace.
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.
Pokud je podřízený úkol reprezentován objektem Task<TResult> namísto objektu Task, můžete zajistit, aby nadřazený úkol čekal na dokončení podřízeného úkolu získáním přístupu k vlastnosti Task<TResult>.Result podřízeného úkolu, i když se jedná o odpojený podřízený úkol. Result vlastnost blokuje, dokud se její úloha nedokončí, jak ukazuje následující příklad.
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
Připojené podřízené úkoly
Na rozdíl od odpojených podřízených úkolů jsou připojené podřízené úkoly úzce synchronizovány s nadřazeným úkolem. Odpojený podřízený úkol v předchozím příkladu můžete změnit na připojený podřízený úkol pomocí možnosti TaskCreationOptions.AttachedToParent v příkazu vytvoření úkolu, jak je znázorněno v následujícím příkladu. V tomto kódu se připojený podřízený úkol dokončí před svým nadřazeným úkolem. Výsledkem je, že výstup z příkladu je stejný při každém spuštění kódu.
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.
Připojené podřízené úlohy můžete použít k vytvoření úzce propojených grafů asynchronních operací.
Podřízený úkol se však může připojit k nadřazenému úkolu pouze v případě, že nadřazený úkol nezakazuje připojující se podřízené úkoly. Nadřazené úkoly mohou explicitně zabránit připojení podřízených úkolů zadáním možnosti TaskCreationOptions.DenyChildAttach v konstruktoru třídy nadřazeného úkolu nebo pomocí metody TaskFactory.StartNew. Rodičovské úkoly implicitně brání připojení podřízených úkolů, pokud byly vytvořeny metodou Task.Run. Následující příklad to ilustruje. Je stejný jako v předchozím příkladu s tím rozdílem, že nadřazený úkol je vytvořen voláním metody Task.Run(Action) místo metody TaskFactory.StartNew(Action). Vzhledem k tomu, že podřízený úkol se nemůže připojit ke svému rodiči, je výstup z příkladu nepředvídatelný. Vzhledem k tomu, že výchozí možnosti vytváření úkolů pro přetížení Task.Run zahrnují TaskCreationOptions.DenyChildAttach, tento příklad je funkčně ekvivalentní prvnímu příkladu v části Odpojené podřízené úkoly.
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.
Výjimky v úkolech podřízených
Pokud nezávislá podřízená úloha vyvolá výjimku, musí být tato výjimka pozorována nebo zpracována přímo v nadřazené úloze, stejně jako v případě jakékoli nenořené úlohy. Pokud připojený podřízený úkol vyvolá výjimku, výjimka se automaticky rozšíří do nadřazené úlohy a zpět do vlákna, které čeká nebo se pokusí získat přístup k vlastnosti úlohy Task<TResult>.Result. Proto pomocí připojených podřízených úloh můžete zpracovat všechny výjimky v jediném okamžiku volání Task.Wait ve volajícím vlákně. Další informace naleznete v tématu Zpracování výjimek.
Zrušení a podřízené úkoly
Zrušení úkolu probíhá ve spolupráci. To znamená, že aby byla zrušitelná každá připojená nebo odpojená podřízená úloha, musí sledovat stav tokenu zrušení. Pokud chcete zrušit rodiče a všechny jeho potomky pomocí jedné žádosti o zrušení, předáte stejný token jako argument všem úkolům a v každém úkolu zadáte logiku, která bude na požadavek reagovat. Další informace naleznete v tématu Zrušení úlohy a Jak na to: Zrušení úlohy a jejích podřízených.
Když rodič zruší
Pokud se nadřazený úkol zruší před spuštěním podřízeného úkolu, podřízený úkol se nikdy nespustí. Pokud se nadřazený úkol zruší po spuštění podřízené úlohy, podřízený úkol pokračuje k dokončení, pokud nemá vlastní logiku zrušení. Další informace naleznete v části Zrušení úkolu.
Když se odpojená podřízená úloha zruší
Pokud se odpojený podřízený úkol zruší pomocí stejného tokenu, který byl předán nadřazenému úkolu, a nadřazený úkol nečeká na podřízený úkol, nedochází k propagaci žádné výjimky, protože neškodné zrušení spolupráce se považuje za výjimku. Toto chování je stejné jako u každého úkolu nejvyšší úrovně.
Když se připojený podřízený úkol zruší
Když se připojený podřízený úkol zruší pomocí stejného tokenu, který byl předán jeho nadřazenému úkolu, rozšíří se TaskCanceledException do spojovacího vlákna uvnitř AggregateException. Musíte počkat na nadřazenou úlohu, abyste mohli zpracovávat všechny neškodné výjimky kromě všech chybných výjimek, které se šíří prostřednictvím grafu připojených podřízených úkolů.
Další informace naleznete v tématu Zpracování výjimek.
Zabránění připojení podřízené úlohy k nadřazené úloze
Neošetřená výjimka vyvolaná podřízeným úkolem se rozšíří do nadřazeného úkolu. Toto chování můžete použít k pozorování všech výjimek podřízených úkolů z jednoho kořenového úkolu místo procházení stromu úkolů. Šíření výjimek však může být problematické, pokud nadřazený úkol neočekává přílohu z jiného kódu. Představte si například aplikaci, která volá komponentu knihovny třetí strany z objektu Task. Pokud komponenta knihovny třetí strany také vytvoří objekt Task a specifikuje TaskCreationOptions.AttachedToParent k připojení k nadřazené úloze, všechny neošetřené výjimky, ke kterým dochází v podřízené úloze, se rozšíří do nadřazeného úkolu. To může vést k neočekávanému chování v hlavní aplikaci.
Chcete-li zabránit připojení podřízeného úkolu k nadřazené úloze, určete možnost TaskCreationOptions.DenyChildAttach při vytváření nadřazeného Task nebo objektu Task<TResult>. Když se úkol pokusí připojit ke svému nadřazenému úkolu a nadřazený úkol určuje možnost TaskCreationOptions.DenyChildAttach, podřízený úkol se nebude moct připojit k nadřazenému úkolu a spustí se stejně, jako kdyby nebyla zadána možnost TaskCreationOptions.AttachedToParent.
Můžete také zabránit tomu, aby se podřízený úkol připojil k nadřazené úlohou, když se nedokončí včas. Vzhledem k tomu, že nadřazený úkol se nedokončí, dokud se nedokončí všechny podřízené úkoly, může dlouhotrvající podřízený úkol způsobit špatný výkon celé aplikace. Příklad, který ukazuje, jak zlepšit výkon aplikace tím, že zabráníte připojení úkolu k nadřazenému úkolu, najdete v tématu Jak: Zabránit připojení podřízené úlohy k nadřazené.