Aracılığıyla paylaş


Görev tabanlı zaman uyumsuz programlama

Görev Paralel Kitaplığı (TPL), zaman uyumsuz bir işlemi temsil eden görevkavramını temel alır. Bazı açılardan, bir görev iş parçacığına veya ThreadPool iş öğesine benzer ancak daha yüksek bir soyutlama düzeyindedir. Görev paralelliği terimi, eşzamanlı olarak çalışan bir veya daha fazla bağımsız görevi ifade eder. Görevler iki birincil avantaj sağlar:

  • Sistem kaynaklarının daha verimli ve daha ölçeklenebilir kullanımı.

    Arka planda görevler, iş parçacığı sayısını belirleyen ve ayarlayan algoritmalarla geliştirilmiş olan ThreadPoolkuyruğa alınır. Bu algoritmalar, aktarım hızını en üst düzeye çıkarmak için yük dengeleme sağlar. Bu işlem görevleri nispeten basit hale getirir ve ayrıntılı paralelliği etkinleştirmek için bunların çoğunu oluşturabilirsiniz.

  • İş parçacığı veya iş öğesiyle mümkün olandan daha fazla programatik kontrol.

    Görevler ve bunların etrafında oluşturulan çerçeve, bekleme, iptal, devamlılık, güçlü özel durum işleme, ayrıntılı durum, özel zamanlama ve daha fazlasını destekleyen zengin bir API kümesi sağlar.

Her iki nedenle de TPL, .NET'te çok iş parçacıklı, zaman uyumsuz ve paralel kod yazmak için tercih edilen API'dir.

Görevleri örtük olarak oluşturma ve çalıştırma

Parallel.Invoke yöntemi, istediğiniz sayıda rastgele deyimi eşzamanlı olarak çalıştırmak için kullanışlı bir yol sağlar. Her iş öğesi için bir Action temsilci aktarmanız yeterli. Bu temsilcileri oluşturmanın en kolay yolu lambda ifadelerini kullanmaktır. Lambda ifadesi adlandırılmış bir yöntemi çağırabilir veya kodu satır içi olarak sağlayabilir. Aşağıdaki örnekte, eşzamanlı olarak çalışan iki görev oluşturan ve başlatan temel bir Invoke çağrısı gösterilmektedir. İlk görev, DoSomeWorkadlı bir yöntemi çağıran bir lambda ifadesiyle temsil edilir ve ikinci görev DoSomeOtherWorkadlı bir yöntemi çağıran bir lambda ifadesiyle temsil edilir.

Not

Bu belgede, TPL'de temsilci tanımlamak için lambda ifadeleri kullanılır. C# veya Visual Basic'teki lambda ifadelerini bilmiyorsanız bkz. PLINQ ve TPL'deki Lambda İfadeleri.

Parallel.Invoke(() => DoSomeWork(), () => DoSomeOtherWork());
Parallel.Invoke(Sub() DoSomeWork(), Sub() DoSomeOtherWork())

Not

Task tarafından arka planda oluşturulan Invoke örneklerinin sayısı, sağlanan temsilci sayısına eşit olmayabilir. TPL, özellikle çok sayıda temsilciyle çeşitli iyileştirmeler içerebilir.

Daha fazla bilgi için bkz. Nasıl yapılır: Paralel İşlemleri Yürütmek için Parallel.Invoke Kullanma.

Görev yürütme üzerinde daha fazla denetim sahibi olmak veya görevden bir değer döndürmek için Task nesnelerle daha açık bir şekilde çalışmanız gerekir.

Görevleri açıkça oluşturma ve çalıştırma

Değer döndürmeyen bir görev, System.Threading.Tasks.Task sınıfı tarafından temsil edilir. Değer döndüren bir görev, System.Threading.Tasks.Task<TResult>'den devralan Task sınıfıyla temsil edilir. Görev nesnesi, altyapı ayrıntılarını işler ve görevin ömrü boyunca çağıran iş parçacığından erişilebilen yöntemler ve özellikler sağlar. Örneğin, herhangi bir zamanda bir görevin Status özelliğine erişerek görevin çalışmaya başladığını, tamamlandığını, iptal edilip edilmediğini veya özel durum oluşturup oluşturmadığını belirleyebilirsiniz. Durum, bir TaskStatus numaralandırması ile gösterilir.

Bir görev oluşturduğunuzda, görevin yürüteceği kodu kapsülleyen bir kullanıcı temsilcisi verirsiniz. Temsilci adlandırılmış bir temsilci, anonim bir yöntem veya lambda ifadesi olarak ifade edilebilir. Lambda ifadeleri, aşağıdaki örnekte gösterildiği gibi adlandırılmış bir yönteme çağrı içerebilir. Örnek, konsol modu uygulaması sona ermeden önce görevin yürütülmesini tamamladığınızdan emin olmak için Task.Wait yöntemine bir çağrı içerir.

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

public class Lambda
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Create a task and supply a user delegate by using a lambda expression.
      Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
      // Start the task.
      taskA.Start();

      // Output a message from the calling thread.
      Console.WriteLine($"Hello from thread '{Thread.CurrentThread.Name}'.");
      taskA.Wait();
   }
}
// The example displays output as follows:
//       Hello from thread 'Main'.
//       Hello from taskA.
// or
//       Hello from taskA.
//       Hello from thread 'Main'.
Imports System.Threading

Namespace Lambda
    Module Example
        Public Sub Main()
            Thread.CurrentThread.Name = "Main"

            ' Create a task and supply a user delegate by using a lambda expression. 
            Dim taskA = New Task(Sub() Console.WriteLine("Hello from taskA."))
            ' Start the task.
            taskA.Start()

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from thread '{0}'.",
                            Thread.CurrentThread.Name)
            taskA.Wait()
        End Sub
    End Module
    ' The example displays output like the following:
    '    Hello from thread 'Main'.
    '    Hello from taskA.
End Namespace

Ayrıca tek bir işlemde görev oluşturmak ve başlatmak için Task.Run yöntemlerini de kullanabilirsiniz. Görevi yönetmek için, Run yöntemleri, geçerli iş parçacığıyla hangi görev zamanlayıcının ilişkilendirildiğine bakılmaksızın varsayılan görev zamanlayıcısını kullanır. Run yöntemleri, görevin oluşturulması ve zamanlanması üzerinde daha fazla denetime ihtiyaç duyulmadığında görevleri oluşturmanın ve başlatmanın tercih edilen yoludur.

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

namespace Run;

public class Example
{
   public static void Main()
   {
      Thread.CurrentThread.Name = "Main";

      // Define and run the task.
      Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));

      // Output a message from the calling thread.
      Console.WriteLine($"Hello from thread '{Thread.CurrentThread.Name}'.");
      taskA.Wait();
   }
}
// The example displays output as follows:
//       Hello from thread 'Main'.
//       Hello from taskA.
// or
//       Hello from taskA.
//       Hello from thread 'Main'.
Imports System.Threading

Namespace Run
    Module Example
        Public Sub Main()
            Thread.CurrentThread.Name = "Main"

            Dim taskA As Task = Task.Run(Sub() Console.WriteLine("Hello from taskA."))

            ' Output a message from the calling thread.
            Console.WriteLine("Hello from thread '{0}'.",
                            Thread.CurrentThread.Name)
            taskA.Wait()
        End Sub
    End Module
    ' The example displays output like the following:
    '    Hello from thread 'Main'.
    '    Hello from taskA.
End Namespace

Tek bir işlemde görev oluşturmak ve başlatmak için TaskFactory.StartNew yöntemini de kullanabilirsiniz. Aşağıdaki örnekte gösterildiği gibi aşağıdaki durumlarda bu yöntemi kullanabilirsiniz:

  • Oluşturma ve zamanlamanın ayrılması gerekmez ve ek görev oluşturma seçeneklerine veya belirli bir zamanlayıcının kullanılmasına ihtiyacınız vardır.

  • Göreve, Task.AsyncState özelliği aracılığıyla erişebileceğiniz ek bilgiyi aktarmanız gerekir.

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

namespace TaskIntro;

class CustomData
{
    public long CreationTime;
    public int Name;
    public int ThreadNum;
}

public class AsyncState
{
    public static void Main()
    {
        Task[] taskArray = new Task[10];
        for (int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = Task.Factory.StartNew((Object obj) =>
            {
                CustomData data = obj as CustomData;
                if (data == null) return;

                data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
            },
            new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks });
        }
        Task.WaitAll(taskArray);
        foreach (var task in taskArray)
        {
            var data = task.AsyncState as CustomData;
            if (data != null)
                Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                  data.Name, data.CreationTime, data.ThreadNum);
        }
    }
}
// The example displays output like the following:
//     Task #0 created at 635116412924597583, ran on thread #3.
//     Task #1 created at 635116412924607584, ran on thread #4.
//     Task #2 created at 635116412924607584, ran on thread #4.
//     Task #3 created at 635116412924607584, ran on thread #4.
//     Task #4 created at 635116412924607584, ran on thread #3.
//     Task #5 created at 635116412924607584, ran on thread #3.
//     Task #6 created at 635116412924607584, ran on thread #4.
//     Task #7 created at 635116412924607584, ran on thread #4.
//     Task #8 created at 635116412924607584, ran on thread #3.
//     Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading

Namespace AsyncState
    Class CustomData
        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Module Example
        Public Sub Main()
            Dim taskArray(9) As Task
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                         Dim data As CustomData = TryCast(obj, CustomData)
                                                         If data Is Nothing Then Return

                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                     End Sub,
                New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
            Next
            Task.WaitAll(taskArray)

            For Each task In taskArray
                Dim data = TryCast(task.AsyncState, CustomData)
                If data IsNot Nothing Then
                    Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                    data.Name, data.CreationTime, data.ThreadNum)
                End If
            Next
        End Sub
    End Module
    ' The example displays output like the following:
    '     Task #0 created at 635116412924597583, ran on thread #3.
    '     Task #1 created at 635116412924607584, ran on thread #4.
    '     Task #2 created at 635116412924607584, ran on thread #4.
    '     Task #3 created at 635116412924607584, ran on thread #4.
    '     Task #4 created at 635116412924607584, ran on thread #3.
    '     Task #5 created at 635116412924607584, ran on thread #3.
    '     Task #6 created at 635116412924607584, ran on thread #4.
    '     Task #7 created at 635116412924607584, ran on thread #4.
    '     Task #8 created at 635116412924607584, ran on thread #3.
    '     Task #9 created at 635116412924607584, ran on thread #4.
End Namespace

Task ve Task<TResult>'in her biri, yöntemi Factoryolarak çağırabilmeniz için varsayılan bir TaskFactoryörneğini döndüren statik bir Task.Factory.StartNew() özelliğini kullanıma sunar. Ayrıca, aşağıdaki örnekte görevler System.Threading.Tasks.Task<TResult>türünde olduğundan, her birinin hesaplamanın sonucunu içeren bir genel Task<TResult>.Result özelliği vardır. Görevler zaman uyumsuz olarak çalışır ve herhangi bir sırada tamamlanabilir. hesaplama tamamlanmadan önce Result özelliğine erişilirse, değer kullanılabilir olana kadar özelliği çağıran iş parçacığını engeller.

using System;
using System.Threading.Tasks;

public class Result
{
   public static void Main()
   {
        Task<Double>[] taskArray = { Task<Double>.Factory.StartNew(() => DoComputation(1.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(100.0)),
                                     Task<Double>.Factory.StartNew(() => DoComputation(1000.0)) };

        var results = new Double[taskArray.Length];
        Double sum = 0;

        for (int i = 0; i < taskArray.Length; i++) {
            results[i] = taskArray[i].Result;
            Console.Write("{0:N1} {1}", results[i],
                              i == taskArray.Length - 1 ? "= " : "+ ");
            sum += results[i];
        }
        Console.WriteLine($"{sum:N1}");
   }

   private static Double DoComputation(Double start)
   {
      Double sum = 0;
      for (var value = start; value <= start + 10; value += .1)
         sum += value;

      return sum;
   }
}
// The example displays the following output:
//        606.0 + 10,605.0 + 100,495.0 = 111,706.0

Namespace Result
    Module Example
        Public Sub Main()
            Dim taskArray() = {Task(Of Double).Factory.StartNew(Function() DoComputation(1.0)),
                Task(Of Double).Factory.StartNew(Function() DoComputation(100.0)),
                Task(Of Double).Factory.StartNew(Function() DoComputation(1000.0))}

            Dim results(taskArray.Length - 1) As Double
            Dim sum As Double

            For i As Integer = 0 To taskArray.Length - 1
                results(i) = taskArray(i).Result
                Console.Write("{0:N1} {1}", results(i),
                    If(i = taskArray.Length - 1, "= ", "+ "))
                sum += results(i)
            Next
            Console.WriteLine("{0:N1}", sum)
        End Sub

        Private Function DoComputation(start As Double) As Double
            Dim sum As Double
            For value As Double = start To start + 10 Step .1
                sum += value
            Next
            Return sum
        End Function
    End Module
    ' The example displays the following output:
    '       606.0 + 10,605.0 + 100,495.0 = 111,706.0
End Namespace

Daha fazla bilgi için bkz. Nasıl Yapılır: Bir Görevden Değer Döndürme.

Temsilci oluşturmak için lambda ifadesi kullandığınızda, kaynak kodunuzda o noktada görünen tüm değişkenlere erişebilirsiniz. Ancak, bazı durumlarda, özellikle döngüler içinde bir lambda değişkeni beklendiği gibi yakalamaz. Her yinelemeden sonra değişime uğradığı için değeri değil, yalnızca değişkenin başvurusunu yakalar. Aşağıdaki örnekte sorun gösterilmektedir. Bir CustomData nesnesinin örneğini oluşturan bir lambda ifadesine döngü sayacı geçirir ve nesnenin tanımlayıcısı olarak döngü sayacını kullanır. Örnekteki çıktıda gösterildiği gibi, her CustomData nesnesi aynı tanımlayıcıya sahiptir.

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

namespace Example.Iterations;

class CustomData
{
   public long CreationTime;
   public int Name;
   public int ThreadNum;
}

public class IterationTwo
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in the loop
      // counter. This produces an unexpected result.
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj) => {
                                                 var data = new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks};
                                                 data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                 Console.WriteLine($"Task #{data.Name} created at {data.CreationTime} on thread #{data.ThreadNum}.");
                                               },
                                              i );
      }
      Task.WaitAll(taskArray);
   }
}
// The example displays output like the following:
//       Task #10 created at 635116418427727841 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427737842 on thread #4.
//       Task #10 created at 635116418427727841 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427747843 on thread #3.
//       Task #10 created at 635116418427737842 on thread #4.
Imports System.Threading

Namespace IterationsTwo
    Class CustomData
        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Module Example
        Public Sub Main()
            ' Create the task object by using an Action(Of Object) to pass in the loop
            ' counter. This produces an unexpected result.
            Dim taskArray(9) As Task
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                         Dim data As New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks}
                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                         Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                         data.Name, data.CreationTime, data.ThreadNum)
                                                     End Sub,
                    i)
            Next
            Task.WaitAll(taskArray)
        End Sub
    End Module
    ' The example displays output like the following:
    '       Task #10 created at 635116418427727841 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427737842 on thread #4.
    '       Task #10 created at 635116418427727841 on thread #3.
    '       Task #10 created at 635116418427747843 on thread #3.
    '       Task #10 created at 635116418427747843 on thread #3.
    '       Task #10 created at 635116418427737842 on thread #4.
End Namespace

Bir göreve oluşturucu aracılığıyla bir durum nesnesi sağlayarak her yinelemedeki değere erişebilirsiniz. Aşağıdaki örnek, lambda ifadesine geçirilen CustomData nesnesi oluşturulurken döngü sayacını kullanarak önceki örneği değiştirir. Örnekteki çıktıda gösterildiği gibi, her CustomData nesnesi artık nesnenin örneği oluşturulduğu sırada döngü sayacının değerine göre benzersiz bir tanımlayıcıya sahiptir.

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

class CustomData
{
   public long CreationTime;
   public int Name;
   public int ThreadNum;
}

public class IterationOne
{
   public static void Main()
   {
      // Create the task object by using an Action(Of Object) to pass in custom data
      // to the Task constructor. This is useful when you need to capture outer variables
      // from within a loop.
      Task[] taskArray = new Task[10];
      for (int i = 0; i < taskArray.Length; i++) {
         taskArray[i] = Task.Factory.StartNew( (Object obj ) => {
                                                  CustomData data = obj as CustomData;
                                                  if (data == null)
                                                     return;

                                                  data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
                                                  Console.WriteLine($"Task #{data.Name} created at {data.CreationTime} on thread #{data.ThreadNum}.");
                                               },
                                               new CustomData() {Name = i, CreationTime = DateTime.Now.Ticks} );
      }
      Task.WaitAll(taskArray);
   }
}
// The example displays output like the following:
//       Task #0 created at 635116412924597583 on thread #3.
//       Task #1 created at 635116412924607584 on thread #4.
//       Task #3 created at 635116412924607584 on thread #4.
//       Task #4 created at 635116412924607584 on thread #4.
//       Task #2 created at 635116412924607584 on thread #3.
//       Task #6 created at 635116412924607584 on thread #3.
//       Task #5 created at 635116412924607584 on thread #4.
//       Task #8 created at 635116412924607584 on thread #4.
//       Task #7 created at 635116412924607584 on thread #3.
//       Task #9 created at 635116412924607584 on thread #4.
Imports System.Threading

Namespace IterationsOne
    Class CustomData
        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Module Example
        Public Sub Main()
            ' Create the task object by using an Action(Of Object) to pass in custom data
            ' to the Task constructor. This is useful when you need to capture outer variables
            ' from within a loop. 
            Dim taskArray(9) As Task
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                         Dim data As CustomData = TryCast(obj, CustomData)
                                                         If data Is Nothing Then Return

                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                         Console.WriteLine("Task #{0} created at {1} on thread #{2}.",
                                                                         data.Name, data.CreationTime, data.ThreadNum)
                                                     End Sub,
                    New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
            Next
            Task.WaitAll(taskArray)
        End Sub
    End Module
    ' The example displays output like the following:
    '       Task #0 created at 635116412924597583 on thread #3.
    '       Task #1 created at 635116412924607584 on thread #4.
    '       Task #3 created at 635116412924607584 on thread #4.
    '       Task #4 created at 635116412924607584 on thread #4.
    '       Task #2 created at 635116412924607584 on thread #3.
    '       Task #6 created at 635116412924607584 on thread #3.
    '       Task #5 created at 635116412924607584 on thread #4.
    '       Task #8 created at 635116412924607584 on thread #4.
    '       Task #7 created at 635116412924607584 on thread #3.
    '       Task #9 created at 635116412924607584 on thread #4.
End Namespace

Bu durum, görev temsilcisine bağımsız değişken olarak geçirilir ve Task.AsyncState özelliği kullanılarak görev nesnesinden erişilebilir. Aşağıdaki örnek, önceki örnekteki bir çeşitlemedir. lambda ifadesine geçirilen AsyncState nesneleri hakkındaki bilgileri görüntülemek için CustomData özelliğini kullanır.

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

namespace TaskIntro;

class CustomData
{
    public long CreationTime;
    public int Name;
    public int ThreadNum;
}

public class AsyncState
{
    public static void Main()
    {
        Task[] taskArray = new Task[10];
        for (int i = 0; i < taskArray.Length; i++)
        {
            taskArray[i] = Task.Factory.StartNew((Object obj) =>
            {
                CustomData data = obj as CustomData;
                if (data == null) return;

                data.ThreadNum = Thread.CurrentThread.ManagedThreadId;
            },
            new CustomData() { Name = i, CreationTime = DateTime.Now.Ticks });
        }
        Task.WaitAll(taskArray);
        foreach (var task in taskArray)
        {
            var data = task.AsyncState as CustomData;
            if (data != null)
                Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                  data.Name, data.CreationTime, data.ThreadNum);
        }
    }
}
// The example displays output like the following:
//     Task #0 created at 635116412924597583, ran on thread #3.
//     Task #1 created at 635116412924607584, ran on thread #4.
//     Task #2 created at 635116412924607584, ran on thread #4.
//     Task #3 created at 635116412924607584, ran on thread #4.
//     Task #4 created at 635116412924607584, ran on thread #3.
//     Task #5 created at 635116412924607584, ran on thread #3.
//     Task #6 created at 635116412924607584, ran on thread #4.
//     Task #7 created at 635116412924607584, ran on thread #4.
//     Task #8 created at 635116412924607584, ran on thread #3.
//     Task #9 created at 635116412924607584, ran on thread #4.
Imports System.Threading

Namespace AsyncState
    Class CustomData
        Public CreationTime As Long
        Public Name As Integer
        Public ThreadNum As Integer
    End Class

    Module Example
        Public Sub Main()
            Dim taskArray(9) As Task
            For i As Integer = 0 To taskArray.Length - 1
                taskArray(i) = Task.Factory.StartNew(Sub(obj As Object)
                                                         Dim data As CustomData = TryCast(obj, CustomData)
                                                         If data Is Nothing Then Return

                                                         data.ThreadNum = Environment.CurrentManagedThreadId
                                                     End Sub,
                New CustomData With {.Name = i, .CreationTime = Date.Now.Ticks})
            Next
            Task.WaitAll(taskArray)

            For Each task In taskArray
                Dim data = TryCast(task.AsyncState, CustomData)
                If data IsNot Nothing Then
                    Console.WriteLine("Task #{0} created at {1}, ran on thread #{2}.",
                                    data.Name, data.CreationTime, data.ThreadNum)
                End If
            Next
        End Sub
    End Module
    ' The example displays output like the following:
    '     Task #0 created at 635116412924597583, ran on thread #3.
    '     Task #1 created at 635116412924607584, ran on thread #4.
    '     Task #2 created at 635116412924607584, ran on thread #4.
    '     Task #3 created at 635116412924607584, ran on thread #4.
    '     Task #4 created at 635116412924607584, ran on thread #3.
    '     Task #5 created at 635116412924607584, ran on thread #3.
    '     Task #6 created at 635116412924607584, ran on thread #4.
    '     Task #7 created at 635116412924607584, ran on thread #4.
    '     Task #8 created at 635116412924607584, ran on thread #3.
    '     Task #9 created at 635116412924607584, ran on thread #4.
End Namespace

Görev Kimliği

Her görev, bir uygulama etki alanında benzersiz olarak tanımlayan bir tamsayı kimliği alır ve Task.Id özelliği kullanılarak erişilebilir. Kimlik, Visual Studio hata ayıklayıcı Paralel Yığınlar ve Görevler pencerelerinde görev bilgilerini görüntülemek için kullanışlıdır. Kimlik, gecikmeli olarak oluşturulur, bu da istekte bulunulana kadar oluşturulmadığı anlamına gelir. Bu nedenle, program her çalıştırıldığında görevin kimliği farklı olabilir. Hata ayıklayıcıda görev kimliklerini görüntüleme hakkında daha fazla bilgi için bkz. Görevler Penceresini Kullanma ve Paralel Yığınlar Penceresini Kullanma .

Görev oluşturma seçenekleri

Görev oluşturan API'lerin çoğu, TaskCreationOptions parametresi kabul eden aşırı yüklemeler sağlar. Bu seçeneklerden birini veya daha fazlasını belirterek, görev zamanlayıcıya iş parçacığı havuzunda görevin nasıl zamanleneceğini bildirirsiniz. Seçenekler bit tabanlı VEYA işlemi kullanılarak birleştirilebilir.

Aşağıdaki örnekte, LongRunning ve PreferFairness seçeneklerine sahip bir görev gösterilmektedir:

var task3 = new Task(() => MyLongRunningMethod(),
                    TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();

Dim task3 = New Task(Sub() MyLongRunningMethod(),
                        TaskCreationOptions.LongRunning Or TaskCreationOptions.PreferFairness)
task3.Start()

Görevler, iş parçacıkları ve kültür

Her iş parçacığı, sırasıyla Thread.CurrentCulture ve Thread.CurrentUICulture özellikleri tarafından tanımlanan ilişkili bir Kültüre ve UI Kültürüne sahiptir. İş parçacığının kültürü, biçimlendirme, ayrıştırma, sıralama ve dize karşılaştırma gibi işlemlerde kullanılır. Kaynak aramada iş parçacığının kullanıcı arabirim kültürü kullanılır.

Sistem kültürü, bir iş parçacığının varsayılan kültürünü ve kullanıcı arabirimi kültürünü tanımlar. Ancak, CultureInfo.DefaultThreadCurrentCulture ve CultureInfo.DefaultThreadCurrentUICulture özelliklerini kullanarak bir uygulama etki alanındaki tüm iş parçacıkları için varsayılan bir kültür belirtebilirsiniz. Bir iş parçacığının kültürünü açıkça ayarlar ve yeni bir iş parçacığı başlatırsanız, yeni iş parçacığı çağıran iş parçacığının kültürünü devralamaz; bunun yerine, kültürü varsayılan sistem kültürüdür. Ancak görev tabanlı programlamada, görev farklı bir iş parçacığında zaman uyumsuz olarak çalıştırıldığında bile görevler çağıran iş parçacığının kültürünü kullanır.

Aşağıdaki örnek basit bir çizim sağlar. Uygulamanın geçerli kültürünü Fransızca (Fransa) olacak şekilde değiştirir. Fransızca (Fransa) zaten geçerli kültürse İngilizce (ABD) olarak değişir. Ardından, yeni kültürde para birimi değerleri olarak biçimlendirilmiş bazı sayıları döndüren formatDelegate adlı bir temsilci çağırır. Temsilcinin bir görev tarafından zaman uyumlu veya zaman uyumsuz olarak çağrılması fark etmeksizin, görev çağıran iş parçacığının kültürünü kullanır.

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

public class Example
{
   public static void Main()
   {
       decimal[] values = { 163025412.32m, 18905365.59m };
       string formatString = "C2";
       Func<String> formatDelegate = () => { string output = String.Format("Formatting using the {0} culture on thread {1}.\n",
                                                                           CultureInfo.CurrentCulture.Name,
                                                                           Thread.CurrentThread.ManagedThreadId);
                                             foreach (var value in values)
                                                output += String.Format("{0}   ", value.ToString(formatString));

                                             output += Environment.NewLine;
                                             return output;
                                           };

       Console.WriteLine($"The example is running on thread {Thread.CurrentThread.ManagedThreadId}");
       // Make the current culture different from the system culture.
       Console.WriteLine($"The current culture is {CultureInfo.CurrentCulture.Name}");
       if (CultureInfo.CurrentCulture.Name == "fr-FR")
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
       else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

       Console.WriteLine($"Changed the current culture to {CultureInfo.CurrentCulture.Name}.\n");

       // Execute the delegate synchronously.
       Console.WriteLine("Executing the delegate synchronously:");
       Console.WriteLine(formatDelegate());

       // Call an async delegate to format the values using one format string.
       Console.WriteLine("Executing a task asynchronously:");
       var t1 = Task.Run(formatDelegate);
       Console.WriteLine(t1.Result);

       Console.WriteLine("Executing a task synchronously:");
       var t2 = new Task<String>(formatDelegate);
       t2.RunSynchronously();
       Console.WriteLine(t2.Result);
   }
}
// The example displays the following output:
//         The example is running on thread 1
//         The current culture is en-US
//         Changed the current culture to fr-FR.
//
//         Executing the delegate synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task asynchronously:
//         Formatting using the fr-FR culture on thread 3.
//         163 025 412,32 €   18 905 365,59 €
//
//         Executing a task synchronously:
//         Formatting using the fr-FR culture on thread 1.
//         163 025 412,32 €   18 905 365,59 €
Imports System.Globalization
Imports System.Threading

Module Example
    Public Sub Main()
        Dim values() As Decimal = {163025412.32D, 18905365.59D}
        Dim formatString As String = "C2"
        Dim formatDelegate As Func(Of String) = Function()
                                                    Dim output As String = String.Format("Formatting using the {0} culture on thread {1}.",
                                                                                         CultureInfo.CurrentCulture.Name,
                                                                                         Thread.CurrentThread.ManagedThreadId)
                                                    output += Environment.NewLine
                                                    For Each value In values
                                                        output += String.Format("{0}   ", value.ToString(formatString))
                                                    Next
                                                    output += Environment.NewLine
                                                    Return output
                                                End Function

        Console.WriteLine("The example is running on thread {0}",
                          Thread.CurrentThread.ManagedThreadId)
        ' Make the current culture different from the system culture.
        Console.WriteLine("The current culture is {0}",
                          CultureInfo.CurrentCulture.Name)
        If CultureInfo.CurrentCulture.Name = "fr-FR" Then
            Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
        Else
            Thread.CurrentThread.CurrentCulture = New CultureInfo("fr-FR")
        End If
        Console.WriteLine("Changed the current culture to {0}.",
                          CultureInfo.CurrentCulture.Name)
        Console.WriteLine()

        ' Execute the delegate synchronously.
        Console.WriteLine("Executing the delegate synchronously:")
        Console.WriteLine(formatDelegate())

        ' Call an async delegate to format the values using one format string.
        Console.WriteLine("Executing a task asynchronously:")
        Dim t1 = Task.Run(formatDelegate)
        Console.WriteLine(t1.Result)

        Console.WriteLine("Executing a task synchronously:")
        Dim t2 = New Task(Of String)(formatDelegate)
        t2.RunSynchronously()
        Console.WriteLine(t2.Result)
    End Sub
End Module

' The example displays the following output:
'
'          The example is running on thread 1
'          The current culture is en-US
'          Changed the current culture to fr-FR.
'
'          Executing the delegate synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task asynchronously:
'          Formatting Imports the fr-FR culture on thread 3.
'          163 025 412,32 €   18 905 365,59 €
'
'          Executing a task synchronously:
'          Formatting Imports the fr-FR culture on thread 1.
'          163 025 412,32 €   18 905 365,59 €

Not

.NET Framework 4.6'dan önceki .NET Framework sürümlerinde, bir görevin kültürü, çağıran iş parçacığının kültürüne değil, üzerinde çalıştığı iş parçacığının kültürüne göre belirlenir. Asenkron görevlerde, görevde kullanılan kültür, çağıran iş parçacığının kültüründen farklı olabilir.

Zaman uyumsuz görevler ve kültür hakkında daha fazla bilgi için CultureInfo makalesindeki "Kültür ve zaman uyumsuz görev tabanlı işlemler" bölümüne bakın.

Görev devamlılıkları oluşturma

Task.ContinueWith ve Task<TResult>.ContinueWith yöntemleri, öncül görev tamamlandığında başlatılacak bir görev belirtmenize olanak sağlar. Devam görevinin yetkilisine, öncül görevin durumunu inceleyebilmesi için öncül göreve bir referans geçirilir. Task<TResult>.Result özelliğinin değerini alarak, önceki çıkışını devam için giriş olarak kullanabilirsiniz.

Aşağıdaki örnekte, getData görevi TaskFactory.StartNew<TResult>(Func<TResult>) yöntemine yapılan bir çağrıyla başlatılır. processData görevi getData tamamlandığında otomatik olarak başlatılır ve displayData tamamlandığında processData başlatılır. getData, processData görevin getData özelliği aracılığıyla Task<TResult>.Result görevin erişebildiği bir tamsayı dizisi oluşturur. processData görevi bu diziyi işler ve sonucu, Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) yöntemine geçen lambda ifadesinin dönüş türünden türetilmiş bir türde döndürür. displayData tamamlandığında processData görevi otomatik olarak yürütülür ve Tuple<T1,T2,T3> lambda ifadesi tarafından döndürülen processData nesnesine displayData görevin processData özelliği aracılığıyla Task<TResult>.Result görev tarafından erişilebilir. displayData görevi, processData görevinin sonucunu alır. Türü benzer şekilde çıkarılan ve Result özelliğinde programın kullanımına sunulan bir sonuç üretir.

using System;
using System.Threading.Tasks;

public class ContinuationOne
{
   public static void Main()
   {
      var getData = Task.Factory.StartNew(() => {
                                             Random rnd = new Random();
                                             int[] values = new int[100];
                                             for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                values[ctr] = rnd.Next();

                                             return values;
                                          } );
      var processData = getData.ContinueWith((x) => {
                                                int n = x.Result.Length;
                                                long sum = 0;
                                                double mean;

                                                for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                                   sum += x.Result[ctr];

                                                mean = sum / (double) n;
                                                return Tuple.Create(n, sum, mean);
                                             } );
      var displayData = processData.ContinueWith((x) => {
                                                    return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                         x.Result.Item1, x.Result.Item2,
                                                                         x.Result.Item3);
                                                 } );
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82

Namespace ContinuationsOne
    Module Example
        Public Sub Main()
            Dim getData = Task.Factory.StartNew(Function()
                                                    Dim rnd As New Random()
                                                    Dim values(99) As Integer
                                                    For ctr = 0 To values.GetUpperBound(0)
                                                        values(ctr) = rnd.Next()
                                                    Next
                                                    Return values
                                                End Function)
            Dim processData = getData.ContinueWith(Function(x)
                                                       Dim n As Integer = x.Result.Length
                                                       Dim sum As Long
                                                       Dim mean As Double

                                                       For ctr = 0 To x.Result.GetUpperBound(0)
                                                           sum += x.Result(ctr)
                                                       Next
                                                       mean = sum / n
                                                       Return Tuple.Create(n, sum, mean)
                                                   End Function)
            Dim displayData = processData.ContinueWith(Function(x)
                                                           Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                                                   x.Result.Item1, x.Result.Item2,
                                                                                   x.Result.Item3)
                                                       End Function)
            Console.WriteLine(displayData.Result)
        End Sub
    End Module
    ' The example displays output like the following:
    '   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
End Namespace

Task.ContinueWith bir örnek yöntemi olduğundan, her bir öncül görev için bir Task<TResult> nesnesi örneği oluşturmak yerine yöntem çağrılarını birbirine zincirleyebilirsiniz. Aşağıdaki örnek, Task.ContinueWith yöntemine çağrıları birbirine bağlaması dışında işlevsel olarak öncekiyle aynıdır. Yöntem çağrıları zinciri tarafından döndürülen Task<TResult> nesnesi son devamlılık görevidir.

using System;
using System.Threading.Tasks;

public class ContinuationTwo
{
   public static void Main()
   {
      var displayData = Task.Factory.StartNew(() => {
                                                 Random rnd = new Random();
                                                 int[] values = new int[100];
                                                 for (int ctr = 0; ctr <= values.GetUpperBound(0); ctr++)
                                                    values[ctr] = rnd.Next();

                                                 return values;
                                              } ).
                        ContinueWith((x) => {
                                        int n = x.Result.Length;
                                        long sum = 0;
                                        double mean;

                                        for (int ctr = 0; ctr <= x.Result.GetUpperBound(0); ctr++)
                                           sum += x.Result[ctr];

                                        mean = sum / (double) n;
                                        return Tuple.Create(n, sum, mean);
                                     } ).
                        ContinueWith((x) => {
                                        return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                             x.Result.Item1, x.Result.Item2,
                                                             x.Result.Item3);
                                     } );
      Console.WriteLine(displayData.Result);
   }
}
// The example displays output similar to the following:
//    N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82

Namespace ContinuationsTwo
    Module Example
        Public Sub Main()
            Dim displayData = Task.Factory.StartNew(Function()
                                                        Dim rnd As New Random()
                                                        Dim values(99) As Integer
                                                        For ctr = 0 To values.GetUpperBound(0)
                                                            values(ctr) = rnd.Next()
                                                        Next
                                                        Return values
                                                    End Function). _
            ContinueWith(Function(x)
                             Dim n As Integer = x.Result.Length
                             Dim sum As Long
                             Dim mean As Double

                             For ctr = 0 To x.Result.GetUpperBound(0)
                                 sum += x.Result(ctr)
                             Next
                             mean = sum / n
                             Return Tuple.Create(n, sum, mean)
                         End Function). _
            ContinueWith(Function(x)
                             Return String.Format("N={0:N0}, Total = {1:N0}, Mean = {2:N2}",
                                                 x.Result.Item1, x.Result.Item2,
                                                 x.Result.Item3)
                         End Function)
            Console.WriteLine(displayData.Result)
        End Sub
    End Module
    ' The example displays output like the following:
    '   N=100, Total = 110,081,653,682, Mean = 1,100,816,536.82
End Namespace

ContinueWhenAll ve ContinueWhenAny yöntemleri birden çok görevden devam etmenizi sağlar.

Daha fazla bilgi için bkz. Devamlılık Görevlerini Kullanarak Görevleri Zincirleme.

Bağımsız alt görevler oluşturma

Bir görevde çalışan kullanıcı kodu yeni bir görev oluşturduğunda ve AttachedToParent seçeneğini belirtmediğinde, yeni görev üst görevle özel bir şekilde eşitlenmez. Bu senkronize edilmemiş görev türüne, bağımsız iç içe görev veya bağımsız alt görevdenir. Aşağıdaki örnekte, bağımsız bir alt görev oluşturan bir görev gösterilmektedir:

var outer = Task.Factory.StartNew(() =>
{
    Console.WriteLine("Outer task beginning.");

    var child = Task.Factory.StartNew(() =>
    {
        Thread.SpinWait(5000000);
        Console.WriteLine("Detached task completed.");
    });
});

outer.Wait();
Console.WriteLine("Outer task completed.");
// The example displays the following output:
//    Outer task beginning.
//    Outer task completed.
//    Detached task completed.
Dim outer = Task.Factory.StartNew(Sub()
                                      Console.WriteLine("Outer task beginning.")
                                      Dim child = Task.Factory.StartNew(Sub()
                                                                            Thread.SpinWait(5000000)
                                                                            Console.WriteLine("Detached task completed.")
                                                                        End Sub)
                                  End Sub)
outer.Wait()
Console.WriteLine("Outer task completed.")
' The example displays the following output:
'     Outer task beginning.
'     Outer task completed.
'    Detached child completed.

Not

Ana görev, bağımsız alt görevin bitmesini beklemez.

Alt görevler oluşturulması

Bir görevde çalışan kullanıcı kodu AttachedToParent seçeneğiyle bir görev oluşturduğunda, yeni görev üst görevin ekli alt görev olarak bilinir. Ana görev, tüm ekli alt görevlerin tamamlanmasını otomatik olarak beklediğinden, yapılandırılmış görev paralelliğini ifade etmek için AttachedToParent seçeneğini kullanabilirsiniz. Aşağıdaki örnekte, 10 ilişkili alt görev oluşturan bir üst görev gösterilmektedir. Örnek, üst görevin tamamlanmasını beklemek için Task.Wait yöntemini çağırır. Ekli alt görevlerin tamamlanmasını açıkça beklemesi gerekmez.

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

public class Child
{
   public static void Main()
   {
      var parent = Task.Factory.StartNew(() => {
                      Console.WriteLine("Parent task beginning.");
                      for (int ctr = 0; ctr < 10; ctr++) {
                         int taskNo = ctr;
                         Task.Factory.StartNew((x) => {
                                                  Thread.SpinWait(5000000);
                                                  Console.WriteLine($"Attached child #{x} completed.");
                                               },
                                               taskNo, TaskCreationOptions.AttachedToParent);
                      }
                   });

      parent.Wait();
      Console.WriteLine("Parent task completed.");
   }
}
// The example displays output like the following:
//       Parent task beginning.
//       Attached child #9 completed.
//       Attached child #0 completed.
//       Attached child #8 completed.
//       Attached child #1 completed.
//       Attached child #7 completed.
//       Attached child #2 completed.
//       Attached child #6 completed.
//       Attached child #3 completed.
//       Attached child #5 completed.
//       Attached child #4 completed.
//       Parent task completed.
Imports System.Threading

Namespace Child
    Module Example
        Public Sub Main()
            Dim parent = Task.Factory.StartNew(Sub()
                                                   Console.WriteLine("Parent task beginning.")
                                                   For ctr As Integer = 0 To 9
                                                       Dim taskNo As Integer = ctr
                                                       Task.Factory.StartNew(Sub(x)
                                                                                 Thread.SpinWait(5000000)
                                                                                 Console.WriteLine("Attached child #{0} completed.",
                                                                                                 x)
                                                                             End Sub,
                                                       taskNo, TaskCreationOptions.AttachedToParent)
                                                   Next
                                               End Sub)
            parent.Wait()
            Console.WriteLine("Parent task completed.")
        End Sub
    End Module
    ' The example displays output like the following:
    '       Parent task beginning.
    '       Attached child #9 completed.
    '       Attached child #0 completed.
    '       Attached child #8 completed.
    '       Attached child #1 completed.
    '       Attached child #7 completed.
    '       Attached child #2 completed.
    '       Attached child #6 completed.
    '       Attached child #3 completed.
    '       Attached child #5 completed.
    '       Attached child #4 completed.
    '       Parent task completed.
End Namespace

Üst görev, diğer görevlerin üst göreve eklenmesini önlemek için TaskCreationOptions.DenyChildAttach seçeneğini kullanabilir. Daha fazla bilgi için bkz. Ekli ve Ayrılmış Alt Görevler.

Görevlerin tamamlanmasını bekliyor

System.Threading.Tasks.Task ve System.Threading.Tasks.Task<TResult> türleri, bir görevin tamamlanmasını beklemenizi sağlayan Task.Wait yöntemlerinin çeşitli aşırı yüklemelerini sağlar. Buna ek olarak, statik Task.WaitAll ve Task.WaitAny yöntemlerine ait fazladan yüklemeler, bir dizi görevden herhangi birinin veya tümünün tamamlanmasını beklemenize olanak sağlar.

Genellikle, şu nedenlerden biri yüzünden bir görev beklerdiniz:

  • Ana iş parçacığı, bir görev tarafından hesaplanan son sonucta bağlıdır.

  • Görevden oluşabilecek özel durumları işlemeniz gerekir.

  • Uygulama, tüm görevlerin yürütülmesi tamamlanmadan sonlandırabilir. Örneğin, konsol uygulamaları Main 'daki tüm zaman uyumlu kod (uygulama giriş noktası) yürütüldükten sonra sonlandırılır.

Aşağıdaki örnekte özel durum işleme içermeyen temel desen gösterilmektedir:

Task[] tasks = new Task[3]
{
    Task.Factory.StartNew(() => MethodA()),
    Task.Factory.StartNew(() => MethodB()),
    Task.Factory.StartNew(() => MethodC())
};

//Block until all tasks complete.
Task.WaitAll(tasks);

// Continue on this thread...
Dim tasks() =
{
    Task.Factory.StartNew(Sub() MethodA()),
    Task.Factory.StartNew(Sub() MethodB()),
    Task.Factory.StartNew(Sub() MethodC())
}

' Block until all tasks complete.
Task.WaitAll(tasks)

' Continue on this thread...

Özel durum işlemeyi gösteren bir örnek için bkz. Özel Durum İşleme.

Bazı aşırı yüklemeler zaman aşımı belirtmenize olanak tanırken, diğerleri de giriş parametresi olarak ek bir CancellationToken alarak bekleme işleminin program aracılığıyla veya kullanıcı girişine yanıt olarak iptal edilebilmesini sağlar.

Bir görevi beklerken, TaskCreationOptions.AttachedToParent seçeneği kullanılarak oluşturulan bu görevin tüm alt görevlerini de örtük olarak beklersiniz. Görev zaten tamamlandıysa, Task.Wait hemen geri döner. Task.Wait yöntemi, görev tamamlandıktan sonra Task.Wait yöntemi çağrılsa bile görev tarafından tetiklenen özel durumları fırlatır.

Görevleri oluşturma

Task ve Task<TResult> sınıfları, birden çok görev oluşturmanıza yardımcı olacak çeşitli yöntemler sağlar. Bu yöntemler ortak desenler uygular ve C#, Visual Basic ve F# tarafından sağlanan zaman uyumsuz dil özelliklerini daha iyi kullanır. Bu bölümde WhenAll, WhenAny, Delayve FromResult yöntemleri açıklanmaktadır.

Task.WhenAll

Task.WhenAll yöntemi zaman uyumsuz olarak birden çok Task veya Task<TResult> nesnesinin bitmesini bekler. Tekdüzen olmayan görev kümelerini beklemenizi sağlayan aşırı yüklenmiş sürümler sağlar. Örneğin, birden çok Task ve Task<TResult> nesnelerinin bir yöntem çağrısından tamamlanmasını bekleyebilirsiniz.

Task.WhenAny

Task.WhenAny yöntemi zaman uyumsuz olarak birden çok Task veya Task<TResult> nesneden birinin bitmesini bekler. Task.WhenAll yönteminde olduğu gibi bu yöntem, tekdüzen olmayan görev kümelerini beklemenizi sağlayan aşırı yüklenmiş sürümler sağlar. WhenAny yöntemi özellikle aşağıdaki senaryolarda kullanışlıdır:

  • Yedekli işlemler: Birçok yolla gerçekleştirilebilecek bir algoritmayı veya işlemi göz önünde bulundurun. önce biten işlemi seçmek ve ardından kalan işlemleri iptal etmek için WhenAny yöntemini kullanabilirsiniz.

  • Araya eklenen işlemler: Her işlem tamamlandıkça sonuçları işlemek için bitirmesi gereken birden çok işlem başlatabilir ve WhenAny yöntemini kullanabilirsiniz. Bir işlem tamamlandıktan sonra bir veya daha fazla görevi başlatabilirsiniz.

  • Kısıtlanmış işlemler: Eşzamanlı işlem sayısını sınırlayarak önceki senaryoyu genişletmek için WhenAny yöntemini kullanabilirsiniz.

  • Süresi dolmuş işlemler: WhenAny yöntemini kullanarak bir veya daha fazla görev ile belirli bir süre sonra biten bir görev (örneğin, Delay yöntemi tarafından döndürülen bir görev) arasında seçim yapabilirsiniz. Delay yöntemi aşağıdaki bölümde açıklanmıştır.

Task.Delay

Task.Delay yöntemi, belirtilen süreden sonra biten bir Task nesnesi oluşturur. Verileri yoklayan döngüler oluşturmak, zaman aşımlarını belirtmek, kullanıcı girişinin işlenmesini geciktirmek vb. için bu yöntemi kullanabilirsiniz.

Görev(T).Sonuçtan

Task.FromResult yöntemini kullanarak, önceden hesaplanan sonucu tutan bir Task<TResult> nesnesi oluşturabilirsiniz. Bu yöntem, bir Task<TResult> nesnesi döndüren zaman uyumsuz bir işlem yaparken ve bu Task<TResult> nesnesinin sonucu önceden hesaplandığında oldukça kullanışlıdır. Önbellekte tutulan zaman uyumsuz indirme işlemlerinin sonuçlarını almak için FromResult kullanan bir örnek için bkz. Nasıl yapılır: Önceden Hesaplanan Görevler Oluşturma.

Görevlerdeki özel durumları işleme

Bir görev bir veya daha fazla özel durum fırlatırsa, bu özel durumlar bir AggregateException özel durumu içinde sarmalanır. Bu istisna, görevle birleşen iş parçacığına geri yayılır. Genellikle, görevin tamam olmasını bekleyen iş parçacığı veya Result özelliğine erişen iş parçacığıdır. Bu davranış, varsayılan olarak işlenmemiş tüm özel durumların işlemi sonlandırması gerektiğini belirten .NET Framework politikası gerektirir. Çağıran kod, try/catch bloğunda aşağıdakilerden birini kullanarak özel durumları işleyebilir:

Katılan iş parçacığı, görev çöp toplama işlemi gerçekleşmeden önce Exception özelliğine erişerek özel durumları da işleyebilir. Bu özelliğe erişerek, işlenmeyen özel durumun nesne sonlandırıldığında işlemi sonlandıran özel durum yayma davranışını tetiklemesini engellersiniz.

Özel durumlar ve görevler hakkında daha fazla bilgi için bkz. Özel Durum İşleme.

Görevleri iptal etme

Task sınıfı işbirliğine dayalı iptali destekler ve .NET Framework 4'te tanıtılan System.Threading.CancellationTokenSource ve System.Threading.CancellationToken sınıfları ile tam olarak tümleşiktir. System.Threading.Tasks.Task sınıfındaki oluşturucuların çoğu giriş parametresi olarak bir CancellationToken nesnesi alır. StartNew ve Run aşırı yüklemelerinin çoğu CancellationToken parametresi de içerir.

belirteci oluşturabilir ve iptal isteğini daha sonra CancellationTokenSource sınıfını kullanarak düzenleyebilirsiniz. Belirteci Task'ye bir parametre olarak geçirin ve iptal isteğine yanıt verme işini yapan kullanıcı temsilcinizde aynı belirteci de kullanın.

Daha fazla bilgi için bkz. Görev İptali ve Görevi ve Alt Öğelerini İptal Etme: Nasıl Yapılır.

TaskFactory sınıfı

TaskFactory sınıfı, görevleri ve devamlılık görevlerini oluşturmak ve başlatmak için ortak desenleri kapsülleyen statik yöntemler sağlar.

Varsayılan TaskFactoryTask sınıfında veya Task<TResult> sınıfında statik özellik olarak erişilebilir. Ayrıca doğrudan bir TaskFactory örneği oluşturabilir ve CancellationToken, TaskCreationOptions seçeneği, TaskContinuationOptions seçeneği veya TaskScheduleriçeren çeşitli seçenekleri belirtebilirsiniz. Görev fabrikasını oluştururken belirtilen seçenekler, Task numaralandırması kullanılarak bir TaskCreationOptions oluşturulmadığı sürece, oluşturduğu tüm görevlere uygulanır; bu durumda, görevin kendi seçenekleri görev fabrikasının seçeneklerini geçersiz kılar.

Temsilcisiz görevler

Bazı durumlarda, kullanıcı temsilciniz yerine bir dış bileşen tarafından gerçekleştirilen zaman uyumsuz bir işlemi kapsüllemek için bir Task'ı kullanmak isteyebilirsiniz. İşlem Zaman Uyumsuz Programlama Modeli Başlangıç/Bitiş desenini temel alırsa, FromAsync yöntemlerini kullanabilirsiniz. Böyle bir durum söz konusu değilse, TaskCompletionSource<TResult> nesnesini kullanarak işlemi bir göreve sarmalayabilir ve böylece Task programlamanın avantajlarından bazılarını elde edebilirsiniz. Örneğin, özel durum yayma ve devamlılık desteği. Daha fazla bilgi için bkz. TaskCompletionSource<TResult>.

Özel zamanlayıcılar

Çoğu uygulama veya kitaplık geliştiricisi, görevin hangi işlemcide çalıştığı, çalışmasını diğer görevlerle nasıl eşitlediği veya System.Threading.ThreadPoolüzerinde nasıl zamanlandığıyla ilgilenmez. Yalnızca konak bilgisayarda mümkün olduğunca verimli bir şekilde yürütülmesini gerektirir. Zamanlama ayrıntıları üzerinde daha ayrıntılı denetime ihtiyacınız varsa, TPL varsayılan görev zamanlayıcıda bazı ayarları yapılandırmanıza ve hatta özel bir zamanlayıcı sağlamanıza olanak tanır. Daha fazla bilgi için bkz. TaskScheduler.

TPL,paralel ve sıralı senaryolarda yararlı olan birkaç yeni genel türe sahiptir. Bunlar, System.Collections.Concurrent ad alanında birkaç iş parçacığı güvenli, hızlı ve ölçeklenebilir koleksiyon sınıfının yanı sıra birkaç yeni senkronizasyon türünü içerir. Örneğin, belirli iş yükleri için öncüllerinden daha verimli olan System.Threading.Semaphore ve System.Threading.ManualResetEventSlim. .NET Framework 4'teki diğer yeni türler, örneğin, System.Threading.Barrier ve System.Threading.SpinLockönceki sürümlerde kullanılamamış işlevler sağlar. Daha fazla bilgi için Paralel Programlama İçin Veri Yapıları'nıinceleyin.

Özel görev türleri

System.Threading.Tasks.Task veya System.Threading.Tasks.Task<TResult>'den devralmamanızı tavsiye ederiz. Bunun yerine, ek verileri veya durumu bir AsyncState veya Task nesnesiyle ilişkilendirmek için Task<TResult> özelliğini kullanmanızı öneririz. Task ve Task<TResult> sınıflarının işlevselliğini genişletmek için uzantı yöntemlerini de kullanabilirsiniz. Uzantı yöntemleri hakkında daha fazla bilgi için bkz. Uzantı Yöntemleri ve Uzantı Yöntemleri.

Task veya Task<TResult>devralmak zorundaysanız, özel görev türünüzün örneklerini oluşturmak için Run veya System.Threading.Tasks.TaskFactory, System.Threading.Tasks.TaskFactory<TResult>veya System.Threading.Tasks.TaskCompletionSource<TResult> sınıflarını kullanamazsınız. Bu sınıflar yalnızca Task ve Task<TResult> nesneleri oluşturduğundan bunları kullanamazsınız. Ayrıca, özel görev türünüzün örneklerini oluşturmak için Task, Task<TResult>, TaskFactoryve TaskFactory<TResult> tarafından sağlanan görev devamlılığı mekanizmalarını kullanamazsınız. Bu sınıflar yalnızca Task ve Task<TResult> nesneleri de oluşturduğundan bunları kullanamazsınız.

Başlık Açıklama
Devam Görevlerini Kullanarak Görevleri Zincirleme Devamlılıkların nasıl çalıştığını açıklar.
Ekli ve Ayrılmış Alt Görevler Ekli ve ayrılmış alt görevler arasındaki farkı açıklar.
Görev İptali Task nesnesinde yerleşik olarak bulunan iptal desteğini açıklar.
Özel Durum İşleme Eşzamanlı iş parçacıklarındaki özel durumların nasıl işleneceğini açıklar.
Nasıl Yapılır: Paralel İşlemleri Yürütmek için Parallel.Invoke Kullanma Invokenasıl kullanılacağını açıklar.
Nasıl Yapılır: Bir Görevden Değer Döndürme Görevlerden değerlerin nasıl geri döndürüleceğini açıklar.
Nasıl Yapılır: Bir Görevi ve Alt Görevlerini İptal Etme Görevlerin nasıl iptal edileceğini açıklar.
Nasıl yapılır: Önceden Hesaplanan Görevler Oluşturma Önbellekte tutulan zaman uyumsuz indirme işlemlerinin sonuçlarını almak için Task.FromResult yönteminin nasıl kullanılacağını açıklar.
Nasıl Yapılır: Paralel Görevlerle İkili Ağaçtan Geçiş Yapma İkili ağaçtan geçiş yapmak için görevlerin nasıl kullanılacağını açıklar.
Nasıl Yapılır: İç İçe Bir Görevi Çözme Unwrap uzantısı yönteminin nasıl kullanılacağını gösterir.
Veri Paralelliği Veriler üzerinde paralel döngüler oluşturmak için For ve ForEach nasıl kullanılacağını açıklar.
paralel programlama .NET Framework paralel programlama için üst düzey düğüm.

Ayrıca bkz.