Vnořené a podřízené úlohy
Vnořené úloha je instance Task , která je vytvořena ve uživatelském delegátu jiného úkolu. Podřízená úloha je vnořená úloha, která je vytvořena s AttachedToParent parametrem. Úloha může vytvořit libovolný počet podřízených nebo vnořené úloh. Jejich počet je omezen pouze systémovými prostředky. Následující příklad ukazuje nadřazenou úlohu, která vytváří jednu jednoduchou vnořenou úloha.
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.
*/
Připojené podřízené úlohy versus odpojeno vnořené úlohy
Nejdůležitější bod s ohledem na podřízený vs. Vnořená úloha je taková úloha, která je v podstatě nezávislá na nadřazené nebo vnější úloze, zatím co připojené podřízený úlohy jsou úzce synchronizovány s nadřazenou úlohou. Pokud změníte inicializaci úlohy, tak aby používala AttachedToParent možnost, jak ukazuje následující příklad
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);
dostanete následující výstup.
' Parent task executing.
' Attached child starting.
' Attached child completing.
' Parent has completed.
Parent task executing.
Attached child starting.
Attached child completing.
Parent has completed.
Je možné použít připojené podřízený úlohy, k vytvoření úzce synchronizovaných grafů asynchronních operací. Ve většině scénářů však doporučujeme použití vnořených úloh, protože mají méně složité vztahy s ostatními úlohami. Proto jsou ve výchozím nastavení úlohy vytvářené uvnitř jiných úloh vnořené a pokud chcete vytvořit podřízenou úlohu, je nutné explicitně určit možnost AttachedToParent.
V následující tabulce jsou uvedeny základní rozdíly mezi dvěma druhy podřízených úkolů.
Kategorie |
Vnořené úlohy |
Připojené podřízené úlohy |
---|---|---|
Vnější úloha (nadřazená) čeká na dokončení vnitřní úlohy. |
Ne |
Ano |
Nadřazená úloha šíří výjimek vyvolané v vnitřních úlohách. |
Ne |
Ano |
Stav nadřazené (vnější) úlohy závisí na stavu podřízené (vnitřní) úlohy. |
Ne |
Ano |
V scénářích pro odpojené úlohy, kde vnořená úloha je Task<TResult>, můžete vynutit čekání nadřazené úlohy na podřízenou úlohu, tak že přistoupíte k vlasnosti Result vnořené úlohy. Vlastnost Result blokuje úlohu dokud není podřízená úloha dokončená.
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.
*/
Výjimky ve vnořených a podřízených úlohách
Pokud vnořená úloha vyvolá výjimku, ta musí být sledována nebo zpracovávána přímo v vnější úloze, stejně tak jak je to u libovolné úlohy bez vnoření. Pokud připojené podřízené výjimku, nadřazeného úkolu a zpět k podprocesu, který čeká nebo pokusí o přístup k úkolu automaticky rozšíří výjimku Result vlastnost. Proto pomocí připojené podřízené úlohy, může zpracovávat všechny výjimky v jednom místě, jmenovitě v volání metody Wait v nadřazeném vláknu. Další informace naleznete v tématu Zpracovávání vyjímek (Task Parallel Library).
Zrušení v podřízených úlohách
Mějte na paměti, že zrušení úloha je kooperativní. Proto, aby se úloha zrušila, každá připojená nebo odpojená podřízená úloha musí sledovat stav token zrušení. Pokud chcete zrušit nadřazenou úlohu a všechny její podřízené úlohy s použitím pouze jedné žádosti, předejte stejný token jako argument na všechny úlohy a v každé úloze vytvořte logiku odpovědi na žádost. Další informace naleznete v tématu Zrušení úlohy a Postupy: Zrušit úlohy a její děti.
Když zrušení vyvolá nadřazená úloha
Pokud nadřazená úloha zruší sama sebe před zahájením podřízené úlohy, podřízená (vnořené) úloha zjevně nebude nikdy spouštěna. Pokud nadřazená úloha zruší sama sebe po tom, co již byla odstartována podřízená nebo vnořená úloha, vnořená úloha poběží do konce ledaže má svojí vlastní logiku zrušení. Další informace naleznete v tématu Zrušení úlohy.
Při zrušení vnořené úlohy
Pokud se odpojená podřízená úloha zruší sama s pomocí stejného tokenu, který byl předán úloze a nadřazená úloha nečeká na podřízenou úlohu, pak nejsou šířeny žádné výjimek, protože všechny vyjímky jsou považovány za neškodné kooperativní zrušení. Toto chování je stejné jako u libovolných úloh nejvyšší úrovně.
Při zrušení podřízené úloha
Pokud připojená úloha zruší sama sebe s použitím stejného token, který byl předán úloze, TaskCanceledException je postoupena do spojovacího vlákna uvnitř AggregateException. Je velmi důležité čekat na nadřazenou úlohu tak, aby bylo možné zpracovat nejen chybové vyjímky, ale také všechny neškodné výjimky, které byly postoupeny nahoru grafem podřízených úloh.
Další informace naleznete v tématu Zpracovávání vyjímek (Task Parallel Library).