Pemrograman asinkron berbasis tugas

Pustaka Paralel Tugas (TPL) didasarkan pada konsep tugas, yang mewakili operasi asinkron. Dalam beberapa cara, tugas menyerupan utas atau ThreadPool item kerja tetapi pada tingkat abstraksi yang lebih tinggi. Istilah paralelisme tugas mengacu pada satu atau beberapa tugas independen yang berjalan bersamaan. Tugas memberikan dua manfaat utama:

  • Penggunaan sumber daya sistem yang lebih efisien dan lebih dapat diskalakan.

    Di balik layar, tugas diantrekan ke ThreadPool, yang telah ditingkatkan dengan algoritma yang menentukan dan menyesuaikan dengan jumlah utas. Algoritma ini memberikan penyeimbangan beban untuk memaksimalkan throughput. Proses ini membuat tugas relatif ringan, dan Anda dapat membuat banyak dari mereka untuk mengaktifkan paralelisme terperinci.

  • Kontrol yang lebih terprogram daripada yang dimungkinkan dengan alur atau item kerja.

    Tugas dan kerangka kerja yang dibangun di sekitarnya menyediakan serangkaian API kaya yang mendukung penantian, pembatalan, kelanjutan, penanganan pengecualian yang kuat, status terperinci, penjadwalan kustom, dan banyak lagi.

Karena kedua alasan tersebut, TPL adalah API pilihan untuk menulis kode multi-utas, asinkron, dan paralel di .NET.

Membuat dan menjalankan tugas secara implisit

Metode ini Parallel.Invoke menyediakan cara mudah untuk menjalankan sejumlah pernyataan arbitrer secara bersamaan. Cukup berikan Action delegasi untuk setiap item pekerjaan. Cara termudah untuk membuat delegasi ini adalah dengan menggunakan ekspresi lambda. Ekspresi lambda dapat memanggil metode bernama atau memberikan kode sebaris. Contoh berikut menunjukkan panggilan dasar Invoke yang membuat dan memulai dua tugas yang berjalan bersamaan. Tugas pertama diwakili oleh ekspresi lambda yang memanggil metode bernama DoSomeWork, dan tugas kedua diwakili oleh ekspresi lambda yang memanggil metode bernama DoSomeOtherWork.

Catatan

Dokumentasi ini menggunakan ekspresi lambda untuk menentukan delegasi di TPL. Jika Anda tidak terbiasa dengan ekspresi lambda di C# atau Visual Basic, lihat Ekspresi Lambda di PLINQ dan TPL.

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

Catatan

Jumlah Task instans yang dibuat di belakang layar Invoke oleh tidak selalu sama dengan jumlah delegasi yang disediakan. TPL mungkin menggunakan berbagai pengoptimalan, terutama dengan sejumlah besar delegasi.

Untuk informasi selengkapnya, lihat Cara: Menggunakan Parallel.Invoke untuk Menjalankan Operasi Paralel.

Untuk kontrol yang lebih besar atas eksekusi tugas atau mengembalikan nilai dari tugas, Anda harus bekerja dengan objek secara Task lebih eksplisit.

Membuat dan menjalankan tugas secara implisit

Tugas yang tidak mengembalikan nilai diwakili oleh System.Threading.Tasks.Task kelas . Tugas yang mengembalikan nilai diwakili oleh System.Threading.Tasks.Task<TResult> kelas, yang mewarisi dari Task. Objek tugas menangani detail infrastruktur dan menyediakan metode dan properti yang dapat diakses dari utas panggilan sepanjang masa pakai tugas. Misalnya, Anda dapat mengakses Status properti tugas kapan saja untuk menentukan apakah tugas telah mulai berjalan, berjalan hingga selesai, dibatalkan, atau telah memberikan pengecualian. Status diwakili oleh TaskStatus enumerasi.

Saat Anda membuat tugas, Anda memberinya delegasi pengguna yang mengenkapsulasi kode yang akan dijalankan tugas. Delegasi dapat dinyatakan sebagai delegasi bernama, metode anonim, atau ekspresi lambda. Ekspresi Lambda dapat berisi panggilan ke metode bernama, seperti yang ditunjukkan dalam contoh berikut. Contohnya mencakup panggilan ke Task.Wait metode untuk memastikan bahwa tugas menyelesaikan eksekusi sebelum aplikasi mode konsol berakhir.

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

Anda juga dapat menggunakan Task.Run metode untuk membuat dan memulai tugas dalam satu operasi. Untuk mengelola tugas, Run metode menggunakan penjadwal tugas default, terlepas dari penjadwal tugas mana yang terkait dengan utas saat ini. Metode Run ini adalah cara yang lebih disukai untuk membuat dan memulai tugas ketika lebih banyak kontrol atas pembuatan dan penjadwalan tugas tidak diperlukan.

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

Anda juga dapat menggunakan TaskFactory.StartNew metode untuk membuat dan memulai tugas dalam satu operasi. Seperti yang ditunjukkan dalam contoh berikut, Anda dapat menggunakan metode ini saat:

  • Pembuatan dan penjadwalan tidak perlu dipisahkan dan Anda memerlukan opsi pembuatan tugas tambahan atau penggunaan penjadwal tertentu.

  • Anda perlu meneruskan status tambahan ke dalam tugas yang dapat Anda ambil melalui propertinya Task.AsyncState .

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 dan Task<TResult> setiap mengekspos properti statis Factory yang mengembalikan instans TaskFactorydefault, sehingga Anda dapat memanggil metode sebagai Task.Factory.StartNew(). Selain itu, dalam contoh berikut, karena tugas berjenis System.Threading.Tasks.Task<TResult>, tugas masing-masing memiliki properti publik Task<TResult>.Result yang berisi hasil komputasi. Tugas berjalan secara asinkron dan mungkin selesai dalam urutan apa pun. Jika Result properti diakses sebelum komputasi selesai, properti memblokir alur panggilan hingga nilai tersedia.

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("{0:N1}", sum);
   }

   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

Untuk informasi selengkapnya, lihat Cara: Mengembalikan Nilai dari Tugas.

Saat Anda menggunakan ekspresi lambda untuk membuat delegasi, Anda memiliki akses ke semua variabel yang terlihat pada saat itu dalam kode sumber Anda. Namun, dalam beberapa kasus, terutama dalam perulangan, lambda tidak menangkap variabel seperti yang diharapkan. Ini hanya menangkap referensi variabel, bukan nilai, karena bermutasi setelah setiap iterasi. Contoh berikut mengilustrasikan masalah. Ini meneruskan penghitung perulangan ke ekspresi lambda yang membuat instans CustomData objek dan menggunakan penghitung perulangan sebagai pengidentifikasi objek. Seperti yang ditunjukkan oleh output dari contoh, setiap CustomData objek memiliki pengidentifikasi yang identik.

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 #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, 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

Anda dapat mengakses nilai pada setiap perulangan dengan memberikan objek status ke tugas melalui konstruktornya. Contoh berikut memodifikasi contoh sebelumnya dengan menggunakan penghitung perulangan saat membuat CustomData objek, yang, pada gilirannya, diteruskan ke ekspresi lambda. Seperti yang ditunjukkan oleh output dari contoh, setiap CustomData objek sekarang memiliki pengidentifikasi unik berdasarkan nilai penghitung perulangan pada saat objek dibuat.

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 #{0} created at {1} on thread #{2}.",
                                                                   data.Name, data.CreationTime, 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

Status ini diteruskan sebagai argumen ke delegasi tugas, dan dapat diakses dari objek tugas dengan menggunakan Task.AsyncState properti. Contoh berikut adalah variasi pada contoh sebelumnya. Ini menggunakan AsyncState properti untuk menampilkan informasi tentang objek yang CustomData diteruskan ke ekspresi lambda.

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

ID Tugas

Setiap tugas menerima ID bilangan bulat yang secara unik mengidentifikasinya di domain aplikasi dan dapat diakses dengan menggunakan Task.Id properti. ID berguna untuk melihat informasi tugas di jendela Tumpukan Paralel dan Tugas debugger Visual Studio. ID dibuat dengan malas, yang berarti id tidak dibuat sampai diminta. Oleh karena itu, tugas mungkin memiliki ID yang berbeda setiap kali program dijalankan. Untuk informasi selengkapnya tentang cara menampilkan ID tugas di debugger, lihat Menggunakan Jendela Tugas dan Menggunakan Jendela Tumpukan Paralel.

Opsi pembuatan tugas

Sebagian besar API yang membuat tugas menyediakan kelebihan beban yang menerima TaskCreationOptions parameter. Dengan menentukan satu atau beberapa opsi ini, Anda memberi tahu penjadwal tugas cara menjadwalkan tugas pada kumpulan alur. Opsi mungkin dikombinasikan dengan menggunakan operasi BITWISE OR .

Contoh berikut menunjukkan tugas yang memiliki LongRunning opsi dan PreferFairness :

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

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

Tugas, alur, dan budaya

Setiap alur memiliki budaya dan budaya UI terkait, yang masing-masing didefinisikan oleh Thread.CurrentCulture properti dan Thread.CurrentUICulture. Budaya utas digunakan dalam operasi seperti, pemformatan, penguraian, pengurutan, dan operasi perbandingan string. Budaya UI alur digunakan dalam pencarian sumber daya.

Budaya sistem mendefinisikan budaya default dan budaya UI utas. Namun, Anda dapat menentukan budaya default untuk semua utas di domain aplikasi dengan menggunakan CultureInfo.DefaultThreadCurrentCulture properti dan CultureInfo.DefaultThreadCurrentUICulture . Jika Anda secara eksplisit menetapkan budaya utas dan meluncurkan utas baru, utas baru tidak mewarisi budaya utas panggilan; sebaliknya, budayanya adalah budaya sistem default. Namun, dalam pemrograman berbasis tugas, tugas menggunakan budaya alur panggilan, bahkan jika tugas berjalan secara asinkron pada alur yang berbeda.

Contoh berikut memberikan ilustrasi sederhana. Ini mengubah budaya aplikasi saat ini menjadi Bahasa Prancis (Prancis). Jika Bahasa Prancis (Prancis) sudah menjadi budaya saat ini, maka akan berubah menjadi bahasa Inggris (Amerika Serikat). Kemudian memanggil delegasi bernama formatDelegate yang mengembalikan beberapa angka yang diformat sebagai nilai mata uang dalam budaya baru. Apakah delegasi dipanggil oleh tugas baik secara sinkron atau asinkron, tugas menggunakan budaya alur panggilan.

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 {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")
          Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
       else
          Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

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

       // 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 €

Catatan

Dalam versi .NET Framework yang lebih lama dari .NET Framework 4.6, budaya tugas ditentukan oleh budaya utas yang dijalankannya, bukan budaya utas panggilan. Untuk tugas asinkron, budaya yang digunakan oleh tugas bisa berbeda dari budaya utas panggilan.

Untuk informasi selengkapnya tentang tugas dan budaya asinkron, lihat bagian "Budaya dan operasi berbasis tugas asinkron" di CultureInfo artikel.

Membuat kelanjutan tugas

Metode Task.ContinueWith dan Task<TResult>.ContinueWith memungkinkan Anda menentukan tugas untuk memulai ketika tugas antecedent selesai. Delegasi tugas kelanjutan diteruskan referensi ke tugas anestesi sehingga dapat memeriksa status tugas anteks. Dan dengan mengambil nilai Task<TResult>.Result properti, Anda dapat menggunakan output antecedent sebagai input untuk kelanjutan.

Dalam contoh berikut, getData tugas dimulai dengan panggilan ke TaskFactory.StartNew<TResult>(Func<TResult>) metode. Tugas processData dimulai secara otomatis setelah getData selesai, dan displayData dimulai setelah processData selesai. getData menghasilkan array bilangan bulat, yang dapat diakses oleh processData tugas melalui getData properti tugas Task<TResult>.Result. Tugas processData memproses array tersebut dan mengembalikan hasil yang jenisnya disimpulkan dari jenis pengembalian ekspresi lambda yang diteruskan ke Task<TResult>.ContinueWith<TNewResult>(Func<Task<TResult>,TNewResult>) metode. Tugas displayData dijalankan secara otomatis ketika processData selesai, dan Tuple<T1,T2,T3> objek yang dikembalikan oleh processData ekspresi lambda dapat diakses oleh displayData tugas melalui processData properti tugas Task<TResult>.Result. Tugas displayData mengambil hasil processData tugas. Ini menghasilkan hasil yang jenisnya disimpulkan dengan cara yang sama, dan yang tersedia untuk program di Result properti.

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

Karena Task.ContinueWith merupakan metode instans, Anda dapat merangkai panggilan metode bersama-sama bukan membuat Task<TResult> instans objek untuk setiap tugas antecedent. Contoh berikut secara fungsional identik dengan yang sebelumnya, kecuali bahwa ia menyatukan panggilan ke Task.ContinueWith metode . Objek Task<TResult> yang dikembalikan oleh rantai panggilan metode adalah tugas kelanjutan akhir.

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

Metode ContinueWhenAll dan ContinueWhenAny memungkinkan Anda melanjutkan dari beberapa tugas.

Untuk informasi selengkapnya, lihat Merangkai Tugas dengan Menggunakan Tugas Kelanjutan.

Membuat tugas turunan yang dilepas

Saat kode pengguna yang berjalan dalam tugas membuat tugas baru dan tidak menentukan AttachedToParent opsi, tugas baru tidak disinkronkan dengan tugas induk dengan cara khusus apa pun. Jenis tugas yang tidak disinkronkan ini disebut tugas berlapis yang dilepas atau tugas turunan yang dilepaskan. Contoh berikut menunjukkan tugas yang membuat satu tugas turunan yang terlepas:

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.

Catatan

Tugas induk tidak menunggu tugas turunan yang dilepas selesai.

Membuat tugas turunan

Saat kode pengguna yang berjalan dalam tugas membuat tugas dengan AttachedToParent opsi , tugas baru dikenal sebagai tugas turunan terlampir dari tugas induk. Anda dapat menggunakan AttachedToParent opsi untuk mengekspresikan paralelisme tugas terstruktur karena tugas induk secara implisit menunggu semua tugas anak terlampir selesai. Contoh berikut menunjukkan tugas induk yang membuat 10 tugas turunan terlampir. Contoh memanggil Task.Wait metode untuk menunggu tugas induk selesai. Tidak perlu secara eksplisit menunggu tugas turunan terlampir selesai.

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 #{0} completed.",
                                                                    x);
                                               },
                                               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

Tugas induk dapat menggunakan TaskCreationOptions.DenyChildAttach opsi untuk mencegah tugas lain melampirkan ke tugas induk. Untuk informasi selengkapnya, lihat Tugas Turunan Terlampir dan Dicopot.

Menunggu tugas selesai

Jenis System.Threading.Tasks.Task dan System.Threading.Tasks.Task<TResult> menyediakan beberapa kelebihan beban Task.Wait metode yang memungkinkan Anda menunggu tugas selesai. Selain itu, kelebihan beban statis Task.WaitAll dan Task.WaitAny metode memungkinkan Anda menunggu salah satu atau semua array tugas selesai.

Biasanya, Anda akan menunggu tugas karena salah satu alasan berikut:

  • Alur utama tergantung pada hasil akhir yang dihitung oleh tugas.

  • Anda harus menangani pengecualian yang mungkin diberikan dari tugas.

  • Aplikasi mungkin berakhir sebelum semua tugas selesai dieksekusi. Misalnya, aplikasi konsol akan berakhir setelah semua kode sinkron di Main (titik entri aplikasi) telah dijalankan.

Contoh berikut menunjukkan pola dasar yang tidak melibatkan penanganan pengecualian:

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...

Untuk contoh yang memperlihatkan penanganan pengecualian, lihat Penanganan Pengecualian.

Beberapa kelebihan beban memungkinkan Anda menentukan waktu habis, dan yang lain mengambil tambahan CancellationToken sebagai parameter input sehingga penantian itu sendiri dapat dibatalkan baik secara terprogram atau sebagai respons terhadap input pengguna.

Ketika Anda menunggu tugas, Anda secara implisit menunggu semua turunan dari tugas yang dibuat dengan menggunakan TaskCreationOptions.AttachedToParent opsi. Task.Wait segera kembali jika tugas telah selesai. Metode Task.Wait akan melemparkan pengecualian apa pun yang dimunculkan oleh tugas, bahkan jika Task.Wait metode dipanggil setelah tugas selesai.

Menyusun tugas

Kelas Task dan Task<TResult> menyediakan beberapa metode untuk membantu Anda menyusun beberapa tugas. Metode ini menerapkan pola umum dan memanfaatkan fitur bahasa asinkron yang lebih baik yang disediakan oleh C#, Visual Basic, dan F#. Bagian ini menjelaskan WhenAllmetode, WhenAny, Delay, dan FromResult.

Task.WhenAll

Metode ini Task.WhenAll secara asinkron menunggu beberapa Task objek atau Task<TResult> selesai. Ini menyediakan versi kelebihan beban yang memungkinkan Anda menunggu serangkaian tugas yang tidak seragam. Misalnya, Anda dapat menunggu beberapa Task objek dan Task<TResult> selesai dari satu panggilan metode.

Task.WhenAny

Metode ini Task.WhenAny secara asinkron menunggu beberapa objek Task atau Task<TResult> selesai. Seperti pada Task.WhenAll metode, ini menyediakan versi kelebihan beban yang memungkinkan Anda menunggu serangkaian tugas yang tidak seragam. Metode WhenAny ini sangat berguna dalam skenario berikut:

  • Operasi redundan: Pertimbangkan algoritma atau operasi yang dapat dilakukan dalam banyak cara. Anda dapat menggunakan WhenAny metode untuk memilih operasi yang selesai terlebih dahulu lalu membatalkan operasi yang tersisa.

  • Operasi interleaved: Anda dapat memulai beberapa operasi yang harus diselesaikan dan menggunakan WhenAny metode untuk memproses hasil saat setiap operasi selesai. Setelah satu operasi selesai, Anda dapat memulai satu atau beberapa tugas.

  • Operasi yang dibatasi: Anda dapat menggunakan WhenAny metode untuk memperluas skenario sebelumnya dengan membatasi jumlah operasi bersamaan.

  • Operasi yang kedaluwarsa: Anda dapat menggunakan WhenAny metode untuk memilih antara satu atau beberapa tugas dan tugas yang selesai setelah waktu tertentu, seperti tugas yang dikembalikan oleh Delay metode . Metode Delay ini dijelaskan di bagian berikut.

Task.Delay

Metode ini Task.Delay menghasilkan Task objek yang selesai setelah waktu yang ditentukan. Anda dapat menggunakan metode ini untuk membangun perulangan yang melakukan polling data, untuk menentukan waktu habis, untuk menunda penanganan input pengguna, dan sebagainya.

Task(T).FromResult

Dengan menggunakan metode ini Task.FromResult, Anda dapat membuat Task<TResult> objek yang menyimpan hasil yang telah dihitung sebelumnya. Metode ini berguna ketika Anda melakukan operasi asinkron yang menghasilkan objek Task<TResult>, dan hasil objek Task<TResult> tersebut sudah dihitung. Untuk contoh yang menggunakan FromResult untuk mengambil hasil operasi pengunduhan asinkron yang disimpan dalam cache, lihat Cara: Membuat Tugas Yang Telah Dihitung Sebelumnya.

Menangani pengecualian dalam tugas

Ketika tugas memberikan satu atau beberapa pengecualian, pengecualian dibungkus dalam AggregateException pengecualian. Pengecualian tersebut disebarluaskan kembali ke utas yang bergabung dengan tugas. Biasanya, utas menunggu tugas selesai atau utas yang mengakses Result properti. Perilaku ini memberlakukan kebijakan .NET Framework bahwa semua pengecualian yang tidak tertangani secara default harus mengakhiri proses. Kode panggilan dapat menangani pengecualian dengan menggunakan salah satu hal berikut ini dalam try/catch blok:

Alur gabungan juga dapat menangani pengecualian dengan mengakses Exception properti sebelum tugas dikumpulkan sampah. Dengan mengakses properti ini, Anda mencegah pengecualian yang tidak tertangani memicu perilaku penyebaran pengecualian yang mengakhiri proses ketika objek diselesaikan.

Untuk informasi selengkapnya tentang pengecualian dan tugas, lihat Penanganan Pengecualian.

Membatalkan tugas

Kelas ini Task mendukung pembatalan koperasi dan terintegrasi penuh dengan kelas System.Threading.CancellationTokenSource dan System.Threading.CancellationToken, yang diperkenalkan dalam .NET Framework 4. Banyak konstruktor di System.Threading.Tasks.Task kelas mengambil CancellationToken objek sebagai parameter input. Banyak kelebihan beban StartNew dan Run juga menyertakan CancellationToken parameter.

Anda dapat membuat token dan mengeluarkan permintaan pembatalan di lain waktu, dengan menggunakan CancellationTokenSource kelas . Teruskan token ke Task sebagai argumen, dan juga referensikan token yang sama dalam delegasi pengguna Anda, yang melakukan pekerjaan menanggapi permintaan pembatalan.

Untuk informasi selengkapnya, lihat Pembatalan Tugas dan Cara: Membatalkan Tugas dan Turunannya.

Kelas TaskFactory

Kelas ini TaskFactory menyediakan metode statis yang merangkum pola umum untuk membuat dan memulai tugas dan tugas kelanjutan.

Default TaskFactory dapat diakses sebagai properti statis pada Task kelas atau Task<TResult> kelas. Anda juga dapat membuat instans TaskFactory secara langsung dan menentukan berbagai opsi yang menyertakan CancellationToken, TaskCreationOptions opsi, TaskContinuationOptions opsi, atau TaskScheduler. Opsi apa pun yang ditentukan ketika Anda membuat pabrik tugas akan diterapkan ke semua tugas yang dibuatnya kecuali Task dibuat dengan menggunakan TaskCreationOptions enumerasi, dalam hal ini opsi tugas mengambil alih dari pabrik tugas.

Tugas tanpa delegasi

Dalam beberapa kasus, Anda mungkin ingin menggunakan Task untuk merangkum beberapa operasi asinkron yang dilakukan oleh komponen eksternal alih-alih delegasi pengguna Anda. Jika operasi didasarkan pada pola Mulai/Akhir Model Pemrograman Asinkron, Anda dapat menggunakan FromAsync metode. Jika tidak demikian, Anda dapat menggunakan TaskCompletionSource<TResult> objek untuk membungkus operasi dalam tugas dan dengan demikian mendapatkan beberapa manfaat dari kemampuan pemrograman Task . Misalnya, dukungan untuk propagasi dan kelanjutan pengecualian. Untuk informasi selengkapnya, lihat TaskCompletionSource<TResult> .

Penjadwal kustom

Sebagian besar pengembang aplikasi atau pustaka tidak peduli prosesor mana yang dijalankan tugas, caranya menyinkronkan pekerjaannya dengan tugas lain, atau bagaimana dijadwalkan pada System.Threading.ThreadPool. Mereka hanya mengharuskan eksekusi seefisien mungkin di komputer host. Jika Anda memerlukan kontrol yang lebih halus atas detail penjadwalan, TPL memungkinkan Anda mengonfigurasi beberapa pengaturan pada penjadwal tugas default, dan bahkan memungkinkan Anda menyediakan penjadwal kustom. Untuk informasi selengkapnya, lihat TaskScheduler .

TPL memiliki beberapa jenis publik baru yang berguna dalam skenario paralel dan berurutan. Ini termasuk beberapa kelas koleksi yang aman, cepat, dan dapat diskalakan di System.Collections.Concurrent namespace layanan dan beberapa jenis sinkronisasi baru. Misalnya, System.Threading.Semaphore dan System.Threading.ManualResetEventSlim, yang lebih efisien daripada pendahulunya untuk jenis beban kerja tertentu. Jenis baru lainnya di .NET Framework 4, misalnya, System.Threading.Barrier dan System.Threading.SpinLock, menyediakan fungsionalitas yang tidak tersedia di rilis sebelumnya. Untuk informasi selengkapnya, lihat Struktur Data untuk Pemrograman Paralel.

Jenis tugas kustom

Kami menyarankan agar Anda tidak mewarisi dari System.Threading.Tasks.Task atau System.Threading.Tasks.Task<TResult>. Sebaliknya, kami sarankan Anda menggunakan AsyncState properti untuk mengaitkan data atau status tambahan dengan objek Task atau Task<TResult>. Anda juga dapat menggunakan metode ekstensi untuk memperluas fungsionalitas Task kelas dan Task<TResult>. Untuk informasi selengkapnya tentang metode ekstensi, lihat Metode Ekstensi dan Metode Ekstensi.

Jika Anda harus mewarisi dari Task atau Task<TResult>, Anda tidak dapat menggunakan Run atau System.Threading.Tasks.TaskFactorykelas , , System.Threading.Tasks.TaskFactory<TResult>atau System.Threading.Tasks.TaskCompletionSource<TResult> untuk membuat instans jenis tugas kustom Anda. Anda tidak dapat menggunakannya karena kelas ini hanya Task membuat objek dan Task<TResult> . Selain itu, Anda tidak dapat menggunakan mekanisme kelanjutan tugas yang disediakan oleh Task, , Task<TResult>TaskFactory, dan TaskFactory<TResult> untuk membuat instans jenis tugas kustom Anda. Anda tidak dapat menggunakannya karena kelas ini juga hanya Task membuat objek dan Task<TResult> .

Judul Deskripsi
Merangkai Tugas dengan Menggunakan Tugas Kelanjutan Menjelaskan cara kerja kelanjutan.
Tugas turunan Terlampir dan Dicopot Menjelaskan perbedaan antara tugas turunan yang dilampirkan dan dicopot.
Pembatalan tugas Menjelaskan dukungan pembatalan yang disertakan dalam Task objek.
Penanganan Pengecualian Menjelaskan bagaimana pengecualian pada alur bersamaan ditangani.
Cara: Menggunakan parallel_invoke untuk menjalankan operasi paralel Menjelaskan cara menggunakan Invoke.
Cara: Mengembalikan Nilai dari Tugas Menjelaskan cara mengembalikan nilai dari tugas.
Cara: Membatalkan Tugas dan Turunannya Menjelaskan cara membatalkan tugas.
Cara: Membuat tugas yang dihitung sebelumnya Dalam artikel ini, Anda akan mempelajari cara menggunakan metode Task.FromResult untuk mengambil hasil operasi pengunduhan asinkron yang disimpan dalam cache.
Cara: Melintasi Pohon Biner dengan Tugas Paralel Menjelaskan cara menggunakan tugas untuk melintasi pohon biner.
Cara: Membuka Bungkus Tugas Berlapis Menunjukkan cara menggunakan Unwrap metode ekstensi.
Paralelisme Data Menjelaskan cara menggunakan For dan ForEach membuat perulangan paralel atas data.
Pemrograman Paralel Simpul tingkat atas untuk pemrograman paralel .NET Framework.

Lihat juga