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.
Pola yang paling umum adalah StartNew, yang membuat dan memulai tugas dalam satu pernyataan.
Saat Anda membuat tugas kelanjutan dari beberapa antecedent, gunakan ContinueWhenAll metode atau ContinueWhenAny metode atau yang setara di Task<TResult> kelas. Untuk informasi selengkapnya, lihat Merangkai Tugas dengan Menggunakan Tugas Kelanjutan.
Untuk mengenkapsulasi Model
BeginX
Pemrograman Asinkron danEndX
metode dalam Task instans atau Task<TResult>, gunakan FromAsync metode. Untuk informasi selengkapnya, lihat TPL dan Pemrograman Asinkron .NET Tradisional.
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 .
Struktur data terkait
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> .
Bagian terkait
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
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk