Eklenen ve Ayrılan Alt Görevler

Alt görev (veya iç içe görev), üst görev olarak bilinen başka bir görevin kullanıcı temsilcisinde oluşturulan bir System.Threading.Tasks.Task örnektir. Alt görev ayrılabilir veya eklenebilir. Ayrılmış alt görev, üst öğesinden bağımsız olarak yürütülen bir görevdir. Ekli alt görev, üst öğesi açıkça eklemeyen veya varsayılan olarak eklenmesini TaskCreationOptions.AttachedToParent yasaklayan seçeneğiyle oluşturulan iç içe yerleştirilmiş bir görevdir. Bir görev, yalnızca sistem kaynaklarıyla sınırlı olmak üzere herhangi bir sayıda ekli ve ayrılmış alt görev oluşturabilir.

Aşağıdaki tabloda, iki tür alt görev arasındaki temel farklar listelenmiştir.

Kategori Ayrılmış alt görevler Ekli alt görevler
Üst öğe alt görevlerin tamamlanmasını bekler. Hayır Evet
Üst öğe, alt görevler tarafından oluşan özel durumları yayılım. Hayır Evet
Üst öğesinin durumu alt öğe durumuna bağlıdır. Hayır Evet

Çoğu senaryoda, diğer görevlerle ilişkileri daha az karmaşık olduğundan, ayrılmış alt görevler kullanmanızı öneririz. Bu nedenle üst görevlerin içinde oluşturulan görevler varsayılan olarak ayrılır ve ekli alt görev oluşturma seçeneğini açıkça belirtmeniz TaskCreationOptions.AttachedToParent gerekir.

Ayrılmış alt görevler

Bir alt görev üst görev tarafından oluşturulsa da, varsayılan olarak üst görevden bağımsızdır. Aşağıdaki örnekte, üst görev basit bir alt görev oluşturur. Örnek kodu birden çok kez çalıştırırsanız, örnekteki çıktının gösterilenden farklı olduğunu ve kodu her çalıştırdığınızda çıkışın değişebileceğini fark edebilirsiniz. Bunun nedeni, üst görev ve alt görevlerin birbirinden bağımsız olarak yürütülmesidir; alt, ayrılmış bir görevdir. Örnek yalnızca üst görevin tamamlanmasını bekler ve konsol uygulaması sonlandırılmadan önce alt görev yürütülmeyebilir veya tamamlanmayabilir.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   public static void Main()
   {
      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.");
   }
}
// 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.

Alt görev nesne yerine bir Task<TResult> nesneyle temsil edilirse, üst görevin, ayrılmış bir Task alt görev olsa bile alt görevin özelliğine erişerek Task<TResult>.Result alt görevin tamamlanmasını bekleyeceğinden emin olabilirsiniz. Result Aşağıdaki örnekte gösterildiği gibi, görevi tamamlanana kadar özellik blokları.

using System;
using System.Threading;
using System.Threading.Tasks;

class Example
{
   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 {0}.", 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

Ekli alt görevler

Ayrılmış alt görevlerin aksine, eklenen alt görevler üst görevle yakından eşitlenir. Önceki örnekte yer alan ayrılmış alt görevi, aşağıdaki örnekte gösterildiği gibi görev oluşturma deyimindeki seçeneği kullanarak TaskCreationOptions.AttachedToParent ekli alt görev olarak değiştirebilirsiniz. Bu kodda, ekli alt görev üst öğesinden önce tamamlanır. Sonuç olarak, kodu her çalıştırdığınızda örnekten elde edilen çıkış aynıdır.

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.

Zaman uyumsuz işlemlerin sıkı eşitlenmiş grafiklerini oluşturmak için ekli alt görevleri kullanabilirsiniz.

Ancak, bir alt görev yalnızca üst öğesi ekli alt görevleri yasaklamıyorsa üst görevine iliştirilebilir. Üst görevler, üst görevin sınıf oluşturucusunda veya yönteminde seçeneğini belirterek TaskCreationOptions.DenyChildAttach alt görevlerin bunlara eklenmesini TaskFactory.StartNew açıkça engelleyebilir. Üst görevler, yöntemi çağrılarak oluşturulduklarında alt görevlerin bunlara eklenmesini Task.Run örtük olarak engeller. Aşağıdaki örnek bunu göstermektedir. Üst görevin yöntemi yerine yöntemi çağrılarak Task.Run(Action) oluşturulması dışında, önceki örnekle TaskFactory.StartNew(Action) aynıdır. Alt görev üst görevine ekleyemediğinden, örnekten gelen çıktı tahmin edilemez. Aşırı yüklemeler için varsayılan görev oluşturma seçenekleri içerdiğinden Task.RunTaskCreationOptions.DenyChildAttach, bu örnek işlevsel olarak "Ayrılmış alt görevler" bölümündeki ilk örneğe eşdeğerdir.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Example
{
   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.

Alt görevlerdeki özel durumlar

Ayrılmış bir alt görev özel durum oluşturursa, bu özel durum, iç içe olmayan tüm görevlerde olduğu gibi doğrudan üst görevde de gözlemlenmeli veya işlenmelidir. Ekli bir alt görev özel durum oluşturursa, özel durum otomatik olarak üst göreve yayılır ve görevin Task<TResult>.Result özelliğine erişmeyi bekleyen veya erişmeye çalışan iş parçacığına geri döner. Bu nedenle, ekli alt görevleri kullanarak, çağrı iş parçacığında çağrısının Task.Wait yalnızca bir noktasında tüm özel durumları işleyebilirsiniz. Daha fazla bilgi için bkz . Özel Durum İşleme.

İptal ve alt görevler

Görev iptali işbirliğine dayalıdır. Diğer bir ifadeyle, iptal edilebilir olması için ekli veya ayrılmış her alt görevin iptal belirtecinin durumunu izlemesi gerekir. Bir iptal isteği kullanarak bir üst öğeyi ve tüm alt öğelerini iptal etmek istiyorsanız, tüm görevlere bağımsız değişken olarak aynı belirteci geçirir ve her görevde her görevde isteğe yanıt verme mantığını sağlarsınız. Daha fazla bilgi için bkz . Görev İptali ve Nasıl yapılır: Görevi ve Alt Öğelerini İptal Etme.

Üst öğe iptal ettiğinde

Üst öğe, alt görevi başlatılmadan önce kendisini iptal ederse, alt öğe hiçbir zaman başlamaz. Bir üst öğe, alt görevi başladıktan sonra kendisini iptal ederse, alt öğe kendi iptal mantığına sahip olmadığı sürece tamamlanmaya çalışır. Daha fazla bilgi için bkz . Görev İptali.

Ayrılmış bir alt görev iptal edildiğinde

Ayrılmış bir alt görev üst öğeye geçirilen aynı belirteci kullanarak kendisini iptal ederse ve üst öğe alt görevi beklemezse, özel durum zararsız işbirliği iptali olarak kabul edildiğinden özel durum yayılmaz. Bu davranış, herhangi bir üst düzey görevin davranışıyla aynıdır.

Ekli alt görev iptal edildiğinde

Ekli bir alt görev, üst görevine geçirilen aynı belirteci kullanarak kendisini iptal ettiğinde, bir TaskCanceledException içindeki birleştiren iş parçacığına AggregateExceptionyayılır. Ekli alt görevlerin grafiği aracılığıyla yayılan tüm hatalı özel durumlara ek olarak tüm zararsız özel durumları işleyebilmek için üst görevi beklemeniz gerekir.

Daha fazla bilgi için bkz . Özel Durum İşleme.

Alt görevin üst görevine eklenmesini engelleme

Alt görev tarafından atılan işlenmeyen bir özel durum üst göreve yayılır. Bu davranışı, bir görev ağacından geçiş yapmak yerine bir kök görevdeki tüm alt görev özel durumlarını gözlemlemek için kullanabilirsiniz. Ancak, bir üst görev başka bir koddan ek beklemediğinde özel durum yayma sorunlu olabilir. Örneğin, bir nesneden üçüncü taraf kitaplık bileşenini çağıran bir Task uygulama düşünün. Üçüncü taraf kitaplık bileşeni de bir Task nesne oluşturur ve bunu üst göreve eklemeyi belirtirse TaskCreationOptions.AttachedToParent , alt görevde oluşan işlenmemiş özel durumlar üst göreve yayılır. Bu, ana uygulamada beklenmeyen davranışlara yol açabilir.

Alt görevin üst görevine eklenmesini önlemek için, üst Task görevi veya Task<TResult> nesneyi oluştururken seçeneğini belirtinTaskCreationOptions.DenyChildAttach. Bir görev üst öğeye eklemeye çalıştığında ve üst öğe seçeneği belirttiğinde TaskCreationOptions.DenyChildAttach , alt görev bir üst öğeye ekleyemez ve seçenek belirtilmemiş gibi TaskCreationOptions.AttachedToParent yürütülür.

Alt görev zamanında bitmediğinde alt görevin üst görevine eklenmesini de engellemek isteyebilirsiniz. Üst görev tüm alt görevler bitene kadar bitmediğinden, uzun süre çalışan bir alt görev genel uygulamanın kötü performans göstermesine neden olabilir. Görevin üst görevine eklenmesini engelleyerek uygulama performansının nasıl geliştirildiğini gösteren bir örnek için bkz . Nasıl yapılır: Alt Görevin Üst Göreve Eklenmesini Engelleme.

Ayrıca bkz.