Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
A gyermekfeladat (vagy beágyazott tevékenység) egy System.Threading.Tasks.Task olyan példány, amely egy másik tevékenység felhasználói delegáltjában jön létre, amelyet szülőtevékenységnek neveznek. A gyermekfeladat leválasztható vagy csatolható. A leválasztott gyermekfeladatok olyan tevékenységek, amelyek a szülőtől függetlenül hajthatók végre. A csatolt gyermekfeladat olyan beágyazott tevékenység, amely azzal a TaskCreationOptions.AttachedToParent beállítással jön létre, amelynek szülője nem kifejezetten vagy alapértelmezés szerint nem tiltja meg a csatolást. A tevékenységek tetszőleges számú csatolt és leválasztott gyermekfeladatot hozhatnak létre, amelyeket csak a rendszererőforrások korlátoznak.
Az alábbi táblázat a gyermekfeladatok két típusa közötti alapvető különbségeket sorolja fel.
| Kategória | Leválasztott gyermekfeladatok | Csatolt gyermekfeladatok |
|---|---|---|
| A szülő megvárja, amíg a gyermekfeladatok befejeződnek. | Nem | Igen |
| A szülő propagálja a gyermekfeladatok által elvetett kivételeket. | Nem | Igen |
| A szülő állapota a gyermek állapotától függ. | Nem | Igen |
A legtöbb esetben azt javasoljuk, hogy használjon leválasztott gyermekfeladatokat, mert a más tevékenységekkel való kapcsolataik kevésbé összetettek. Ez az oka annak, hogy a szülőfeladatokon belül létrehozott feladatok alapértelmezés szerint függetlenek, és meg kell adnia a TaskCreationOptions.AttachedToParent lehetőséget egy csatolt gyermekfeladat létrehozásához.
Leválasztott gyermekfeladatok
Bár a gyermekfeladatot egy szülőfeladat hozza létre, alapértelmezés szerint független a szülőtevékenységtől. Az alábbi példában egy szülőfeladat létrehoz egy egyszerű gyermekfeladatot. Ha többször futtatja a példakódot, észreveheti, hogy a példa kimenete eltér a megjelenítetttől, és hogy a kimenet minden alkalommal megváltozhat, amikor futtatja a kódot. Ennek az az oka, hogy a szülő- és gyermekfeladatok függetlenül végzik a feladatukat; a gyermek egy önálló feladat. A példa csak a szülőfeladat befejezésére vár, és előfordulhat, hogy a gyermekfeladat nem fut vagy fejeződik be a konzolalkalmazás leállása előtt.
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.
Ha a gyermekfeladatot Task<TResult> objektum jelöli Task objektum helyett, akkor a gyermek Task<TResult>.Result tulajdonságának elérésével biztosíthatja, hogy a szülőfeladat megvárja, amíg a gyermek befejeződik, még akkor is, ha az egy leválasztott gyermekfeladat. A Result tulajdonság letiltja, amíg a tevékenység befejeződik, ahogy az alábbi példa is mutatja.
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
Csatolt gyermekfeladatok
A leválasztott gyermekfeladatokkal ellentétben a csatolt gyermekfeladatok szorosan szinkronizálódnak a szülővel. Az előző példában szereplő leválasztott gyermekfeladatot egy csatolt gyermekfeladatra módosíthatja a TaskCreationOptions.AttachedToParent tevékenységlétrehozás utasításában található beállítással, ahogyan az az alábbi példában is látható. Ebben a kódban a csatolt gyermekfeladat a szülő előtt fejeződik be. Ennek eredményeképpen a példa kimenete minden alkalommal ugyanaz, amikor futtatja a kódot.
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.
A csatolt gyermekfeladatokkal szorosan szinkronizált diagramokat hozhat létre az aszinkron műveletekről.
A gyermekfeladatok azonban csak akkor csatolhatók a szülőhöz, ha szülője nem tiltja a csatolt gyermekfeladatokat. A szülőtevékenységek explicit módon megakadályozhatják, hogy a gyermekfeladatok hozzájuk csatolódjanak a TaskCreationOptions.DenyChildAttach szülőtevékenység osztálykonstruktorában vagy a TaskFactory.StartNew metódusban megadott beállítás megadásával. A szülőfeladatok implicit módon megakadályozzák, hogy a gyermekfeladatok hozzájuk kapcsolódjanak, ha a Task.Run metódus meghívásával jönnek létre. Az alábbi példa ezt szemlélteti. Ez megegyezik az előző példával, azzal a kivételsel, hogy a szülőfeladat a metódus meghívásával Task.Run(Action) jön létre a TaskFactory.StartNew(Action) metódus helyett. Mivel a gyermekfeladat nem csatolható a szülőhöz, a példa kimenete kiszámíthatatlan. Mivel a túlterhelések alapértelmezett tevékenységlétrehozási Task.Run beállításai közé tartozik TaskCreationOptions.DenyChildAttach, ez a példa funkcionálisan egyenértékű a "Leválasztott gyermekfeladatok" szakaszban szereplő első példával.
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.
Kivételek a gyermekfeladatokban
Ha egy leválasztott gyermekfeladat kivételt okoz, ezt a kivételt közvetlenül a szülőfeladatban kell megfigyelni vagy kezelni, ugyanúgy, mint bármely nem beágyazott tevékenység esetében. Ha egy csatolt gyermekfeladat kivételt jelez, a rendszer automatikusan propagálja a kivételt a szülőtevékenységre, majd vissza a szálra, amely a tevékenység tulajdonságához Task<TResult>.Result várakozik vagy megpróbál hozzáférni. Ezért a csatolt gyermekfeladatok használatával az összes kivételt a hívás Task.Wait egy pontján kezelheti a hívószálon. További információ: Kivételkezelés.
Lemondási és gyermekfeladatok
A feladat törlése együttműködésen alapul. Vagyis, hogy lemondható legyen, minden csatolt vagy leválasztott gyermekfeladatnak figyelnie kell a törlési jogkivonat állapotát. Ha egy szülőt és annak összes gyermekét egy lemondási kéréssel szeretné megszüntetni, akkor ugyanazt a tokent adja át minden tevékenység argumentumaként, és minden tevékenységben megadja a logikát a kérés megválaszolásához. További információért lásd: Feladat törlése és Útmutató: Feladat és annak alfeladatainak törlése.
Amikor a szülő megszakítja a műveletet
Ha egy szülő lemondja magát a gyermekfeladat megkezdése előtt, a gyermek soha nem indul el. Ha egy szülő lemondja magát, miután a gyermekfeladat már elindult, a gyermek a befejezésig fut, hacsak nincs saját törlési logikája. További információ: Tevékenység lemondása.
Ha egy leválasztott gyermekfeladat megszakítja a műveletet
Ha egy leválasztott gyermekfeladat ugyanazzal a jogkivonattal szakítja meg magát, amelyet a szülőnek átadott, és a szülő nem várja meg a gyermekfeladatot, a rendszer nem propagálja a kivételt, mert a kivételt jóindulatú együttműködés-megszakításként kezeli a rendszer. Ez a viselkedés ugyanaz, mint bármely legfelső szintű tevékenység esetén.
Amikor egy csatolt gyermekfeladat lemondásra kerül
Amikor egy csatolt gyermekfeladat ugyanazzal a jogkivonattal mondja le magát, amelyet a szülőfeladatnak átadott, TaskCanceledException kerül továbbításra a csatlakozó szálban egy AggregateException. Meg kell várnia a szülőfeladatot, hogy az összes jóindulatú kivételt kezelni tudja, valamint az összes, hiba következtében keletkezett kivételt is, amelyeket a csatolt gyermekfeladatok gráfjában továbbítanak.
További információ: Kivételkezelés.
Gyermekfeladat szülőhöz csatolásának megakadályozása
A gyermekfeladat által dobott kezeletlen kivétel a szülőfeladatra továbbítódik. Ezzel a viselkedéssel megfigyelheti az összes gyermekfeladat-kivételt egy gyökérfeladatból ahelyett, hogy egy tevékenységfán halad át. A kivételpropagálás azonban problémás lehet, ha egy szülőfeladat nem vár mellékletet más kódból. Vegyük például azt az alkalmazást, amely külső erőforrástár-összetevőt hív meg egy Task objektumból. Ha a külső könyvtár-összetevő is létrehoz egy Task objektumot, és úgy adja meg TaskCreationOptions.AttachedToParent, hogy az a szülőfeladathoz legyen csatolva, a gyermekfeladatban előforduló nem kezelt kivételek a szülőre propagálódnak. Ez váratlan viselkedéshez vezethet a fő alkalmazásban.
Ha meg szeretné akadályozni, hogy egy gyermekfeladat a szülőfeladatához kapcsolódjon, adja meg a TaskCreationOptions.DenyChildAttach szülő Task vagy Task<TResult> objektum létrehozásakor a beállítást. Amikor egy feladat megpróbál csatolódni a szülőhöz, és a szülő meghatározza a TaskCreationOptions.DenyChildAttach beállítást, a gyermekfeladat nem tud majd csatolódni a szülőhöz, és ugyanúgy fog futni, mintha a TaskCreationOptions.AttachedToParent beállítás nem lett volna meghatározva.
Azt is megakadályozhatja, hogy a gyermekfeladat a szülőhöz legyen csatolva, ha a gyermekfeladat nem fejeződik be időben. Mivel a szülőtevékenységek nem fejeződnek be, amíg az összes gyermektevékenység be nem fejeződik, a hosszú ideig futó gyermektevékenységek miatt az általános alkalmazás rosszul teljesít. Egy példa, amely bemutatja, hogyan javíthatja az alkalmazás teljesítményét azáltal, hogy megakadályozza, hogy egy tevékenység a szülőtevékenységéhez kapcsolódjon, lásd : A gyermekfeladat csatolásának megakadályozása a szülőhöz.