Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Dalam .NET, pola asinkron berbasis tugas adalah pola desain asinkron yang direkomendasikan untuk pengembangan baru. Ini didasarkan pada tipe Task dan Task<TResult> di namespace System.Threading.Tasks, yang mewakili operasi-operasi asinkron.
Penamaan, parameter, dan jenis pengembalian
TAP menggunakan satu metode untuk mewakili inisiasi dan penyelesaian operasi asinkron. Pendekatan ini berbeda dengan pola Model Pemrograman Asinkron (APM atau IAsyncResult) dan Pola Asinkron Berbasis Peristiwa (EAP). APM memerlukan metode Begin dan End. EAP memerlukan metode yang memiliki akhiran Async dan juga memerlukan satu atau beberapa event, jenis delegasi penanganan event, dan jenis turunan dari EventArg. Metode asinkron dalam TAP menyertakan Async akhiran setelah nama operasi untuk metode yang mengembalikan jenis yang dapat ditunda, seperti Task, Task<TResult>, ValueTask, dan ValueTask<TResult>. Misalnya, operasi asinkron Get yang mengembalikan Task<String> dapat diberi nama GetAsync. Jika Anda menambahkan metode TAP ke kelas yang sudah berisi nama metode EAP dengan Async akhiran, gunakan akhiran TaskAsync sebagai gantinya. Misalnya, jika kelas sudah memiliki GetAsync metode, gunakan nama GetTaskAsync. Jika metode memulai operasi asinkron tetapi tidak mengembalikan jenis yang dapat ditunggu, namanya harus dimulai dengan Begin, Start, atau beberapa kata kerja lain untuk menyarankan bahwa metode ini tidak mengembalikan atau melemparkan hasil operasi.
Metode TAP mengembalikan System.Threading.Tasks.Task atau System.Threading.Tasks.Task<TResult>, berdasarkan apakah metode sinkron yang sesuai mengembalikan kekosongan atau jenis TResult.
Parameter metode TAP harus cocok dengan parameter rekan sinkronnya dan harus disediakan dalam urutan yang sama. Namun, out parameter dan ref dikecualikan dari aturan ini dan harus dihindari sepenuhnya. Setiap data yang dikembalikan oleh parameter out atau ref seharusnya menjadi bagian dari TResult yang dikembalikan oleh Task<TResult>, dan menggunakan tuple atau struktur data kustom untuk mengakomodasi beberapa nilai. Selain itu, pertimbangkan untuk menambahkan parameter CancellationToken bahkan jika versi sinkron dari metode TAP tidak menyediakannya.
Metode yang dikhususkan secara eksklusif untuk pembuatan, manipulasi, atau kombinasi tugas (di mana niat asinkron metode jelas dalam nama metode atau atas nama jenis tempat metode berada) tidak perlu mengikuti pola penamaan ini. Metode seperti itu sering disebut sebagai combinator. Contoh combinator termasuk WhenAll dan WhenAny, dan dibahas di bagian Menggunakan Combinator berbasis Tugas Bawaan dari artikel Menggunakan Pola Asinkron Berbasis Tugas.
Untuk contoh bagaimana sintaks TAP berbeda dari sintaks yang digunakan dalam pola pemrograman asinkron warisan seperti Model Pemrograman Asinkron (APM) dan Pola Asinkron Berbasis Peristiwa (EAP), lihat Pola Pemrograman Asinkron.
Perilaku asinkron, jenis pengembalian, dan penamaan
Kata kunci async tidak memaksa metode untuk berjalan secara asinkron pada utas lain. Ini memungkinkan await, dan metode berjalan secara sinkron sampai mencapai objek awaiting yang belum selesai. Jika metode tidak mencapai awaitable yang tidak lengkap, metode tersebut dapat diselesaikan secara sinkron.
Untuk sebagian besar API, lebih suka jenis pengembalian ini:
- Gunakan Task untuk operasi asinkron yang tidak menghasilkan nilai.
- Gunakan Task<TResult> untuk operasi asinkron yang menghasilkan nilai.
- Gunakan ValueTask atau ValueTask<TResult> hanya ketika pengukuran menunjukkan tekanan alokasi dan kapan konsumen dapat menangani batasan penggunaan tambahan.
Pastikan penamaan TAP tetap dapat diprediksi.
- Gunakan sufiks
Asyncuntuk metode yang mengembalikan tipe awaitable. - Jangan tambahkan
Asyncke metode sinkron. - Tambahkan kelebihan beban baru
MethodNameAsyncbersama 'MethodName' yang ada. Jangan hapus atau ganti nama API sinkron. Mempertahankan keduanya memungkinkan pengguna layanan bermigrasi dengan kemampuan mereka sendiri tanpa perubahan drastis.
Memulai operasi asinkron
Metode asinkron yang didasarkan pada TAP dapat melakukan sejumlah kecil pekerjaan secara sinkron, seperti memvalidasi argumen dan memulai operasi asinkron, sebelum mengembalikan tugas yang dihasilkan. Jaga agar pekerjaan sinkron tetap minimum sehingga metode asinkron dapat kembali dengan cepat. Alasan pengembalian cepat meliputi:
- Anda dapat memanggil metode asinkron dari utas antarmuka pengguna (UI), dan pekerjaan sinkron yang berjalan lama dapat membahayakan responsivitas aplikasi.
- Anda dapat meluncurkan beberapa metode asinkron secara bersamaan. Oleh karena itu, setiap pekerjaan jangka panjang dalam bagian sinkron dari metode asinkron dapat menunda inisiasi operasi asinkron lainnya, sehingga mengurangi manfaat konkurensi.
Dalam beberapa kasus, jumlah pekerjaan yang diperlukan untuk menyelesaikan operasi kurang dari jumlah pekerjaan yang diperlukan untuk meluncurkan operasi secara asinkron. Membaca dari aliran di mana operasi baca dapat dipenuhi oleh data yang sudah di-buffer dalam memori adalah contoh skenario seperti itu. Dalam kasus seperti itu, operasi mungkin selesai secara sinkron, dan mungkin mengembalikan tugas yang sudah selesai.
Pengecualian
Metode asinkron harus melemparkan pengecualian langsung dari panggilan metode asinkron hanya sebagai respons terhadap kesalahan penggunaan. Kesalahan penggunaan tidak boleh terjadi dalam kode produksi. Misalnya, jika penerusan referensi null (Nothing di Visual Basic) sebagai argumen metode menyebabkan kondisi kesalahan (biasanya diwakili oleh pengecualian ArgumentNullException), Anda dapat memodifikasi kode panggilan untuk memastikan bahwa referensi null tidak pernah diteruskan. Untuk semua kesalahan lainnya, tetapkan pengecualian yang terjadi ketika metode asinkron berjalan ke tugas yang dikembalikan, bahkan jika metode asinkron kebetulan selesai secara sinkron sebelum tugas itu dikembalikan. Biasanya, tugas berisi paling banyak satu pengecualian. Namun, jika tugas mewakili beberapa operasi (misalnya, WhenAll), beberapa pengecualian mungkin dikaitkan dengan satu tugas.
Lingkungan target
Saat menerapkan metode TAP, Anda dapat menentukan di mana eksekusi asinkron terjadi. Anda dapat memilih untuk menjalankan beban kerja pada kumpulan utas, menerapkannya dengan menggunakan I/O asinkron (tanpa terikat ke utas untuk sebagian besar eksekusi operasi), menjalankannya pada utas tertentu (seperti utas UI), atau menggunakan sejumlah konteks potensial. Metode TAP bahkan mungkin tidak memiliki apa pun untuk dijalankan, dan mungkin hanya mengembalikan Task yang mewakili terjadinya kondisi di tempat lain dalam sistem (misalnya, tugas yang mewakili data yang tiba pada struktur data yang diantrekan).
Pemanggil metode TAP dapat memblokir menunggu metode TAP selesai dengan menunggu tugas yang dihasilkan secara sinkron, atau dapat menjalankan kode tambahan (kelanjutan) ketika operasi asinkron selesai. Pembuat kode kelanjutan memiliki kontrol atas tempat kode tersebut dijalankan. Anda dapat membuat kode kelanjutan baik secara eksplisit, melalui metode pada kelas Task (misalnya, ContinueWith), atau secara implisit, dengan menggunakan dukungan bahasa yang dibangun di atas kelanjutan (misalnya, await di C#, Await di Visual Basic, AwaitValue di F#).
Status tugas
Kelas ini Task menyediakan siklus hidup untuk operasi asinkron, dan siklus tersebut TaskStatus diwakili oleh enumerasi. Untuk mendukung kasus-kasus khusus dari jenis yang diturunkan dari Task dan Task<TResult>, dan untuk mendukung pemisahan antara konstruksi dan penjadwalan, kelas Task mengekspos metode Start. Tugas yang dibuat oleh konstruktor publik Task disebut sebagai tugas dingin, karena mereka memulai siklus hidup mereka dalam status tidak terjadwal Created dan hanya dijadwalkan ketika Start dipanggil pada instans ini.
Semua tugas lain memulai siklus hidup mereka dalam keadaan panas, yang berarti bahwa operasi asinkron yang mereka wakili sudah dimulai dan status tugas mereka adalah nilai enumerasi selain TaskStatus.Created. Semua tugas yang dikembalikan dari metode TAP harus diaktifkan. Jika metode TAP secara internal menggunakan konstruktor tugas untuk menginstansiasi tugas yang akan dikembalikan, metode TAP harus memanggil objek StartTask sebelum mengembalikannya. Konsumen metode TAP dapat dengan yakin menganggap bahwa tugas yang dikembalikan aktif dan tidak boleh mencoba memanggil Start pada Task apa pun yang dikembalikan oleh metode TAP. Memanggil Start pada tugas aktif mengakibatkan InvalidOperationException pengecualian.
Untuk panduan tentang masalah masa hidup dan kepemilikan fire-and-forget setelah aktivasi tugas, lihat Menjaga metode asinkron tetap hidup.
Pembatalan (opsional)
Di TAP, pembatalan bersifat opsional untuk pelaksana metode asinkron dan konsumen metode asinkron. Jika operasi memungkinkan pembatalan, operasi akan mengekspos kelebihan beban metode asinkron yang menerima token pembatalan (CancellationToken instans). Menurut konvensi, parameter diberi nama cancellationToken.
public static Task ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
Public Function ReadAsync(buffer As Byte(), offset As Integer, count As Integer,
cancellationToken As CancellationToken) As Task
Operasi asinkron memantau token ini untuk permintaan pembatalan. Jika menerima permintaan pembatalan, sistem tersebut mungkin memilih untuk mematuhinya dan membatalkan operasi. Jika permintaan pembatalan mengakibatkan pekerjaan berakhir sebelum waktunya, metode TAP mengembalikan tugas yang berakhir dalam Canceled status; tidak ada hasil yang tersedia dan tidak ada pengecualian yang dilemparkan. Status Canceled dianggap sebagai status akhir (selesai) untuk tugas, bersama dengan status Faulted dan RanToCompletion . Oleh karena itu, jika tugas dalam status Canceled, propertinya IsCompleted mengembalikan true. Ketika tugas selesai dalam Canceled status, setiap kelanjutan yang terdaftar dengan tugas dijadwalkan atau dijalankan, kecuali opsi kelanjutan seperti NotOnCanceled ditentukan untuk menolak kelanjutan. Kode apa pun yang menunggu tugas yang dibatalkan secara asinkron melalui penggunaan fitur bahasa, masih akan berjalan dan menerima OperationCanceledException atau pengecualian yang diturunkan darinya. Kode yang diblokir secara sinkron menunggu tugas melalui metode seperti Wait dan WaitAll juga terus berjalan dengan pengecualian.
Jika token pembatalan meminta pembatalan sebelum metode TAP yang menerima token tersebut dipanggil, maka metode TAP harus mengembalikan sebuah tugas Canceled. Namun, jika pembatalan diminta saat operasi asinkron berjalan, operasi asinkron tidak perlu menerima permintaan pembatalan. Tugas yang dikembalikan harus berakhir dalam status Canceled hanya jika operasi berakhir sebagai akibat dari permintaan pembatalan. Jika pembatalan diminta tetapi hasil atau pengecualian masih dihasilkan, tugas harus berakhir dalam status RanToCompletion atau Faulted .
Untuk metode asinkron yang ingin mengekspos kemampuan untuk dibatalkan terlebih dahulu dan yang terpenting, Anda tidak perlu memberikan kelebihan beban yang tidak menerima token pembatalan. Untuk metode yang tidak dapat dibatalkan, jangan berikan kelebihan beban yang menerima token pembatalan; ini membantu menunjukkan kepada pemanggil apakah metode target benar-benar dapat dibatalkan. Kode konsumen yang tidak menginginkan pembatalan dapat memanggil metode yang menerima CancellationToken dan memberikan None sebagai nilai argumen. None secara fungsional setara dengan default CancellationToken.
Pelaporan kemajuan (opsional)
Beberapa operasi asinkron mendapat manfaat dari memberikan pemberitahuan kemajuan. Biasanya, gunakan pemberitahuan ini untuk memperbarui antarmuka pengguna dengan informasi tentang kemajuan operasi asinkron.
Di TAP, tangani kemajuan melalui IProgress<T> antarmuka. Teruskan antarmuka ini ke metode asinkron sebagai parameter, biasanya bernama progress. Ketika Anda menyediakan antarmuka kemajuan pada saat memanggil metode asinkron, Anda membantu menghilangkan kondisi balapan yang dihasilkan dari penggunaan yang salah. Kondisi race ini terjadi ketika penangan peristiwa terdaftar secara tidak benar setelah operasi dimulai dan melewatkan pembaruan. Yang lebih penting, antarmuka kemajuan mendukung berbagai implementasi perkembangan, sesuai dengan yang ditentukan oleh kode yang menggunakan. Misalnya, kode yang mengonsumsi mungkin hanya peduli dengan pembaruan kemajuan terbaru, atau mungkin ingin menyimpan semua pembaruan, melakukan tindakan untuk setiap pembaruan, atau mengontrol apakah pemanggilan dialihkan ke utas tertentu. Semua opsi ini dapat dicapai dengan menggunakan implementasi antarmuka yang berbeda, disesuaikan dengan kebutuhan konsumen tertentu. Seperti halnya pembatalan, implementasi TAP harus memberikan IProgress<T> parameter hanya jika API mendukung pemberitahuan kemajuan.
Misalnya, jika metode yang ReadAsync dibahas sebelumnya dalam artikel ini dapat melaporkan kemajuan menengah dalam bentuk jumlah byte yang dibaca sejauh ini, panggilan balik kemajuan bisa menjadi IProgress<T> antarmuka:
public static Task ReadAsync(byte[] buffer, int offset, int count,
IProgress<long> progress)
Public Function ReadAsync(buffer As Byte(), offset As Integer, count As Integer,
progress As IProgress(Of Long)) As Task
FindFilesAsync Jika metode mengembalikan daftar semua file yang memenuhi pola pencarian tertentu, panggilan balik kemajuan dapat memberikan perkiraan persentase pekerjaan yang selesai dan kumpulan hasil parsial saat ini. Ini dapat memberikan informasi ini dengan salah satu dari tuple:
public static Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<Tuple<double, ReadOnlyCollection<List<FileInfo>>>> progress)
Public Function FindFilesAsync(
pattern As String,
progress As IProgress(Of Tuple(Of Double, ReadOnlyCollection(Of List(Of FileInfo))))) As Task(Of ReadOnlyCollection(Of FileInfo))
atau dengan jenis data yang khusus untuk API:
public static Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
string pattern,
IProgress<FindFilesProgressInfo> progress)
Public Function FindFilesAsync(
pattern As String,
progress As IProgress(Of FindFilesProgressInfo)) As Task(Of ReadOnlyCollection(Of FileInfo))
Dalam kasus terakhir, jenis data khusus biasanya diakhiri dengan ProgressInfo.
Jika implementasi TAP memberikan kelebihan beban yang menerima progress parameter, mereka harus mengizinkan argumen menjadi null. Jika Anda melewati null, tidak ada kemajuan yang dilaporkan. Implementasi TAP harus melaporkan kemajuan ke Progress<T> objek secara sinkron, yang memungkinkan metode asinkron untuk memberikan kemajuan dengan cepat. Ini juga memungkinkan penerima kemajuan untuk menentukan bagaimana dan di mana informasi sebaiknya ditangani. Misalnya, instance kemajuan dapat memilih untuk mengatur panggilan balik dan mengaktifkan peristiwa pada konteks sinkronisasi yang ditangkap.
Implementasi dari IProgress<T>
.NET menyediakan Progress<T> kelas , yang mengimplementasikan IProgress<T>. Kelas Progress<T> dinyatakan sebagai berikut:
public class Progress<T> : IProgress<T>
{
public Progress();
public Progress(Action<T> handler);
protected virtual void OnReport(T value);
public event EventHandler<T>? ProgressChanged;
}
Instans dari Progress<T> mengungkapkan suatu peristiwa ProgressChanged, yang dipicu setiap kali operasi asinkron melaporkan pembaruan kemajuan. Peristiwa ProgressChanged dipicu pada objek SynchronizationContext yang ditangkap oleh instans Progress<T> ketika diinisialisasi. Jika tidak ada konteks sinkronisasi yang tersedia, konteks default yang menargetkan kumpulan utas digunakan. Anda dapat mendaftarkan handler dengan peristiwa ini. Untuk kenyamanan, Anda juga dapat memberikan satu handler kepada Progress<T> konstruktor. Handler ini berulah sama seperti penanganan aktivitas untuk peristiwa tersebut ProgressChanged . Pembaruan kemajuan dimunculkan secara asinkron untuk menghindari penundaan operasi asinkron saat penanganan aktivitas dijalankan. Implementasi lain IProgress<T> dapat memilih untuk menerapkan semantik yang berbeda.
Memilih kelebihan beban yang akan disediakan
Jika implementasi TAP menggunakan parameter opsional CancellationToken dan opsional IProgress<T> , hal ini berpotensi memerlukan hingga empat kelebihan beban:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
public Task MethodNameAsync(…, IProgress<T> progress);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken cancellationToken) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
Namun, banyak implementasi TAP tidak memberikan kemampuan pembatalan atau kemajuan, sehingga memerlukan satu metode:
public Task MethodNameAsync(…);
Public MethodNameAsync(…) As Task
Jika implementasi TAP mendukung pembatalan atau kemajuan tetapi tidak keduanya, itu mungkin memberikan dua kelebihan beban:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, CancellationToken cancellationToken);
// … or …
public Task MethodNameAsync(…);
public Task MethodNameAsync(…, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken) As Task
' … or …
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, progress As IProgress(Of T)) As Task
Jika implementasi TAP mendukung pembatalan dan kemajuan, implementasi tersebut mungkin mengekspos keempat kelebihan beban. Namun, mungkin hanya menyediakan dua hal berikut:
public Task MethodNameAsync(…);
public Task MethodNameAsync(…,
CancellationToken cancellationToken, IProgress<T> progress);
Public MethodNameAsync(…) As Task
Public MethodNameAsync(…, cancellationToken As CancellationToken,
progress As IProgress(Of T)) As Task
Untuk mengimbangi dua kombinasi intermediate yang hilang, pengembang dapat meneruskan None atau nilai default CancellationToken sebagai parameter cancellationToken dan null sebagai parameter progress.
Jika Anda mengharapkan setiap penggunaan metode TAP untuk mendukung pembatalan atau kemajuan, Anda dapat menghilangkan kelebihan beban yang tidak menerima parameter yang relevan.
Jika Anda memutuskan untuk mengekspos beberapa overload untuk membuat pembatalan atau pelaporan kemajuan menjadi opsional, overload yang tidak mendukung pembatalan atau pelaporan kemajuan harus berperilaku seolah-olah overload tersebut meneruskan None untuk pembatalan atau null untuk pelaporan kemajuan ke overload yang mendukung parameter ini.
Artikel terkait
- Pola Pemrograman Asinkron — Memperkenalkan tiga pola untuk melakukan operasi asinkron: Pola Asinkron Berbasis Tugas (TAP), Model Pemrograman Asinkron (APM), dan Pola Asinkron Berbasis Peristiwa (EAP).
- Menimplementasikan Pola Asinkron berbasis Tugas — Menjelaskan cara menerapkan TAP dalam tiga cara: dengan menggunakan pengompilasi C# dan Visual Basic di Visual Studio, secara manual, atau melalui kombinasi metode kompilator dan manual.
- Mengonsumsi Pola Asinkron berbasis Tugas — Menjelaskan bagaimana Anda dapat menggunakan tugas dan panggilan balik untuk mencapai penantian tanpa memblokir.
- Interop dengan Pola dan Jenis Asinkron Lainnya — Menjelaskan cara menggunakan TAP untuk mengimplementasikan Model Pemrograman Asinkron (APM) dan Pola Asinkron Berbasis Peristiwa (EAP).