Megosztás a következőn keresztül:


Csatolt és leválasztott gyermekfeladatok

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.

Lásd még