Aracılığıyla paylaş


Bağlı ve Ayrılmış Alt Görevler

alt görev (veya iç içe görev), System.Threading.Tasks.Taskolarak bilinen başka bir görevin kullanıcı temsilcisinde oluşturulan bir örnektir. Bir alt görev bağımsız veya bağlı olabilir. Bağımsız alt görev , ana görevinden bağımsız olarak yürütülen bir görevdir. ekli alt görev, üst öğesi bu görevin eklenmesini açıkça veya varsayılan olarak yasaklamayan TaskCreationOptions.AttachedToParent 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.

İki tür çocuk görevi arasındaki temel farklar aşağıdaki tabloda listelenmiştir.

Kategori Bağımsız alt görevler Ekli çocuk görevler
Ana görev alt görevlerin tamamlanmasını bekler. Hayı Evet
Üst düzey görev, alt görevlerin attığı istisnaları yayar. Hayı Evet
Ebeveynin durumu çocuğun durumuna bağlıdır. Hayı 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, ana görevlerin içinde oluşturulan görevler varsayılan olarak bağımsız hale gelir ve bağlı alt görev oluşturmak için TaskCreationOptions.AttachedToParent seçeneğini açıkça belirtmeniz gerekir.

Bağımsız 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, ebeveyn 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, ana görev ve alt görevlerin birbirinden bağımsız olarak yürütülmesidir; alt görev bağımsız 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 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.

Alt görev, Task<TResult> nesnesi yerine Task nesnesiyle temsil edildiğinde, ayrılmış bir alt görev olsa bile, alt görevin Task<TResult>.Result özelliğine erişerek, ana görevin alt görevin tamamlanmasını bekleyeceğinden emin olunabilir. Result özelliği, aşağıdaki örnekte gösterildiği gibi görevi tamamlanana kadar engeller.

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

Ekli çocuk görevler

Ayrılmış alt görevlerin aksine, bağlı alt görevler üst görevle yakından senkronize edilir. Önceki örnekteki ayrılmış alt görevi, aşağıdaki örnekte gösterildiği gibi görev oluşturma deyimindeki TaskCreationOptions.AttachedToParent seçeneğini kullanarak ekli alt görev olarak değiştirebilirsiniz. Bu kodda, ekli alt görev ana görevden ö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.

Asenkron işlemlerin düzenli grafiklerini oluşturmak için bağlı alt görevleri kullanabilirsiniz.

Ancak, bir alt görev, yalnızca ana görevi ekli alt görevleri yasaklamıyorsa ana görevine iliştirilebilir. Üst görevler, üst görevin sınıf oluşturucusunda veya TaskCreationOptions.DenyChildAttach yönteminde TaskFactory.StartNew seçeneğini belirterek alt görevlerin bunlara eklenmesini açıkça engelleyebilir. Üst görevler, Task.Run yöntemi çağrılarak oluşturulduklarında alt görevlerin bunlara eklenmesini örtük olarak engeller. Aşağıdaki örnekte bu gösterilmektedir. Üst görevin Task.Run(Action) yöntemi yerine TaskFactory.StartNew(Action) yöntemi çağrılarak oluşturulması dışında, önceki örnekle aynıdır. Çocuk görev, ebeveynine bağlanamadığı için, örnek çıktısı tahmin edilemez. Task.Run aşırı yüklemeleri için varsayılan görev oluşturma seçenekleri TaskCreationOptions.DenyChildAttachiçerdiğinden, 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 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.

Alt görevlerdeki istisnalar

Ayrılmış bir alt görev bir özel durum oluşturursa, bu özel durum, doğrudan bağlı olmayan görevlerde olduğu gibi ana görevde 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ğini 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 Task.Wait çağrısının 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 tek seferde iptal etmek istiyorsanız, tüm görevlere aynı belirteci bağımsız değişken olarak geçirir ve her görevde isteğe yanıt verme mantığını sağlarsınız. Daha fazla bilgi için bkz. Görev İptali ve Görevi ve Alt Öğelerini İptal Etme: Nasıl Yapılır.

Ana öğe iptal ettiğinde

Ebeveyn, alt görev başlamadan önce kendisini iptal ederse, alt görev hiçbir zaman başlamaz. Bir ebeveyn, alt görevi başladıktan sonra kendisini iptal ederse, alt görev, kendi iptal mantığına sahip olmadığı sürece tamamlanmaya devam eder. 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 verilen aynı belirteci kullanarak kendisini iptal ederse ve üst öğe alt görevi beklemezse, özel durum doğası gereği zararsız bir işbirliği iptali olarak kabul edildiğinden, bu özel durum yayılmaz. Bu davranış, herhangi bir üst düzey görevin davranışıyla aynıdır.

Ekli alt görev iptal olduğunda

Ekli bir alt görev, üst görevine geçirilen aynı belirteci kullanarak kendisini iptal ettiğinde, TaskCanceledException bir AggregateExceptioniçindeki birleştiren iş parçacığına yayı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.

Çocuk görevin üst görevine eklenmesini önleme

Alt görev tarafından atılan işlenmeyen bir özel durum ebeveyn göreve yayılır. Bu davranışı, bir görev ağaçında dolaşmak yerine bir kök görevden tüm alt görev istisnalarını gözlemlemek için kullanabilirsiniz. Ancak, bir ebeveyn görev başka bir koddan katılım beklemediğinde, özel durum iletimi sorunlu olabilir. Örneğin, bir Task nesnesinden üçüncü taraf kitaplık bileşenini çağıran bir uygulama düşünün. Üçüncü taraf kütüphane bileşeni de bir Task nesnesi oluşturur ve bu nesneyi üst göreve eklemek için TaskCreationOptions.AttachedToParent belirtirse, alt görevde meydana gelen işlenmeyen özel durumlar üst göreve yayılır. Bu, ana uygulamada beklenmeyen davranışlara yol açabilir.

Alt görevin ebeveyn görevine eklenmesini önlemek için, ebeveyn TaskCreationOptions.DenyChildAttach veya Task nesnesini oluştururken Task<TResult> seçeneğini belirtin. Bir görev ebeveynine bağlanmaya çalıştığında ve ebeveyn TaskCreationOptions.DenyChildAttach seçeneğini belirttiğinde, alt görev ebeveynine bağlanamaz ve sanki TaskCreationOptions.AttachedToParent seçeneği belirtilmemiş gibi çalıştırılır.

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

Ayrıca bkz.