Bagikan melalui


Gambaran Umum Pola Asinkron Berbasis Peristiwa

Aplikasi yang melakukan banyak tugas secara bersamaan, namun tetap responsif terhadap interaksi pengguna, sering kali memerlukan desain yang menggunakan beberapa utas. Namespace layanan System.Threading menyediakan semua alat yang diperlukan untuk membuat aplikasi multiutas berkinerja tinggi, tetapi menggunakan alat ini secara efektif membutuhkan pengalaman yang signifikan dengan rekayasa perangkat lunak multiutas. Untuk aplikasi multiutas yang relatif sederhana, komponen BackgroundWorker menyediakan solusi yang mudah. Untuk aplikasi asinkron yang lebih canggih, pertimbangkan untuk mengimplementasikan kelas yang mematuhi Pola Asinkron Berbasis Peristiwa.

Pola Asinkron berbasis Peristiwa menyediakan keuntungan dari aplikasi multiutas sambil menyembunyikan banyak masalah kompleks yang melekat dalam desain multiutas. Menggunakan kelas yang mendukung pola ini dapat memungkinkan Anda untuk:

  • Melakukan tugas yang memakan waktu, seperti unduhan dan operasi database, "di latar belakang," tanpa mengganggu aplikasi Anda.

  • Menjalankan beberapa operasi secara bersamaan, menerima pemberitahuan saat masing-masing selesai.

  • Menunggu hingga sumber daya tersedia tanpa menghentikan ("memblokir") aplikasi Anda.

  • Berkomunikasi dengan operasi asinkron yang tertunda menggunakan model peristiwa dan delegasi yang familier. Untuk informasi selengkapnya tentang menggunakan penanganan aktivitas dan delegasi, lihat Peristiwa.

Kelas yang mendukung Pola Asinkron berbasis Peristiwa akan memiliki satu atau beberapa metode bernama MethodNameAsync. Metode ini dapat mencerminkan versi sinkron, yang mana melakukan operasi yang sama pada rangkaian saat ini. Kelas dapat juga memiliki peristiwa MethodNameCompleted dan mungkin memiliki metode MethodNameAsyncCancel (atau hanya CancelAsync).

PictureBox adalah komponen khusus yang mendukung Pola Asinkron berbasis Peristiwa. Anda dapat mengunduh gambar secara sinkron dengan memanggil metode Load, tetapi jika gambar besar, atau jika koneksi jaringan lambat, aplikasi Anda akan berhenti merespons sampai operasi pengunduhan selesai dan panggilan untuk Load kembali.

Jika Anda ingin aplikasi Anda terus berjalan saat gambar dimuat, Anda dapat memanggil metode LoadAsync dan menangani peristiwa LoadCompleted, sama seperti Anda menangani peristiwa lainnya. Ketika Anda memanggil metode LoadAsync, aplikasi Anda akan terus berjalan saat unduhan berlanjut pada utas terpisah ("di latar belakang"). Penanganan aktivitas Anda akan dipanggil saat operasi pemuatan gambar selesai, dan penanganan aktivitas Anda dapat memeriksa parameter AsyncCompletedEventArgs untuk menentukan apakah pengunduhan berhasil diselesaikan.

Pola Asinkron berbasis Peristiwa mengharuskan operasi asinkron dapat dibatalkan, dan kontrol PictureBox mendukung persyaratan ini dengan metode CancelAsync. Memanggil CancelAsync mengirimkan permintaan untuk menghentikan pengunduhan yang tertunda, dan ketika tugas dibatalkan, peristiwa LoadCompleted dimunculkan.

Perhatian

Ada kemungkinan bahwa unduhan akan selesai tepat saat permintaan CancelAsync dibuat, jadi Cancelled mungkin tidak mencerminkan permintaan untuk membatalkan. Ini disebut kondisi balapan dan merupakan masalah umum dalam pemrograman multiutas. Untuk informasi selengkapnya tentang masalah dalam pemrograman multiutas, lihat Praktik Terbaik Pengaluran Terkelola.

Karakteristik Pola Asinkron berbasis Peristiwa

Pola Asinkron Berbasis Peristiwa dapat mengambil beberapa bentuk, tergantung pada kompleksitas operasi yang didukung oleh kelas tertentu. Kelas paling sederhana mungkin memiliki metode MethodNameAsync tunggal dan peristiwa MethodNameCompleted yang sesuai. Kelas yang lebih kompleks mungkin memiliki beberapa metode MethodNameAsync, masing-masing dengan peristiwa MethodNameCompleted yang sesuai, serta versi sinkron dari metode ini. Kelas dapat mendukung pembatalan secara opsional, pelaporan kemajuan, dan hasil bertambah bertahap untuk setiap metode asinkron.

Metode asinkron juga dapat mendukung beberapa panggilan yang tertunda (beberapa pemanggilan bersamaan), memungkinkan kode Anda untuk memanggilnya beberapa kali sebelum menyelesaikan operasi lain yang tertunda. Menangani situasi ini dengan benar mungkin memerlukan aplikasi Anda melacak penyelesaian setiap operasi.

Contoh Pola Asinkron berbasis Peristiwa

Komponen SoundPlayer dan PictureBox mewakili implementasi sederhana dari Pola Asinkron berbasis Peristiwa. Komponen WebClient dan BackgroundWorker mewakili implementasi yang lebih kompleks dari Pola Asinkron berbasis Peristiwa.

Di bawah ini adalah contoh deklarasi kelas yang sesuai dengan pola:

Public Class AsyncExample  
    ' Synchronous methods.  
    Public Function Method1(ByVal param As String) As Integer
    Public Sub Method2(ByVal param As Double)
  
    ' Asynchronous methods.  
    Overloads Public Sub Method1Async(ByVal param As String)
    Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object)
    Public Event Method1Completed As Method1CompletedEventHandler  
  
    Overloads Public Sub Method2Async(ByVal param As Double)
    Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object)
    Public Event Method2Completed As Method2CompletedEventHandler  
  
    Public Sub CancelAsync(ByVal userState As Object)
  
    Public ReadOnly Property IsBusy () As Boolean  
  
    ' Class implementation not shown.  
End Class  
public class AsyncExample  
{  
    // Synchronous methods.  
    public int Method1(string param);  
    public void Method2(double param);  
  
    // Asynchronous methods.  
    public void Method1Async(string param);  
    public void Method1Async(string param, object userState);  
    public event Method1CompletedEventHandler Method1Completed;  
  
    public void Method2Async(double param);  
    public void Method2Async(double param, object userState);  
    public event Method2CompletedEventHandler Method2Completed;  
  
    public void CancelAsync(object userState);  
  
    public bool IsBusy { get; }  
  
    // Class implementation not shown.  
}  

Kelas AsyncExample fiktif memiliki dua metode, keduanya mendukung pemanggilan sinkron dan asinkron. Kelebihan beban sinkron berperilaku seperti panggilan metode apa pun dan menjalankan operasi pada utas panggilan; jika operasi memakan waktu, bisa jadi ada penundaan yang nyata sebelum panggilan kembali. Kelebihan beban asinkron akan memulai operasi pada utas lain lalu segera kembali, memungkinkan utas panggilan untuk melanjutkan saat operasi dijalankan "di latar belakang."

Kelebihan Beban Metode Asinkron

Ada dua kemungkinan kelebihan beban untuk operasi asinkron: pemanggilan tunggal dan pemanggilan ganda. Anda dapat membedakan kedua bentuk ini dengan tanda tangan metodenya: formulir multi-pemanggilan memiliki parameter tambahan yang disebut userState. Formulir ini memungkinkan kode Anda untuk memanggil Method1Async(string param, object userState) beberapa kali tanpa menunggu operasi asinkron yang tertunda selesai. Apabila, di sisi lain, Anda mencoba memanggil Method1Async(string param) sebelum pemanggilan sebelumnya selesai, metode ini memunculkan InvalidOperationException.

Parameter userState untuk kelebihan beban multi-pemanggilan memungkinkan Anda untuk membedakan antara operasi asinkron. Anda memberikan nilai unik (misalnya, GUID atau hash) untuk setiap panggilan ke Method1Async(string param, object userState), dan saat setiap operasi selesai, penanganan aktivitas Anda dapat menentukan instans operasi mana yang memunculkan peristiwa penyelesaian.

Melacak Operasi Tertunda

Jika Anda menggunakan kelebihan beban multi-pemanggilan, kode Anda harus melacak objek userState (ID tugas) untuk tugas yang tertunda. Untuk setiap panggilan ke Method1Async(string param, object userState), Anda biasanya akan menghasilkan objek baru userState yang unik dan menambahkannya ke koleksi. Saat tugas yang terkait dengan objek userState ini memunculkan peristiwa penyelesaian, implementasi metode penyelesaian Anda akan memeriksa AsyncCompletedEventArgs.UserState dan menghapusnya dari koleksi Anda. Digunakan dengan cara ini, parameter userState mengambil peran ID tugas.

Catatan

Anda harus berhati-hati memberikan nilai unik untuk userState dalam panggilan Anda ke kelebihan beban multi-pemanggilan. ID tugas yang tidak unik akan menyebabkan kelas asinkron menampilkan ArgumentException.

Membatalkan Operasi Tertunda

Penting untuk dapat membatalkan operasi asinkron kapan saja sebelum selesai. Kelas yang mengimplementasikan Pola Asinkron berbasis Peristiwa akan memiliki metode CancelAsync (jika hanya ada satu metode asinkron) atau metode MethodNameAsyncCancel (jika ada beberapa metode asinkron).

Metode yang memungkinkan beberapa pemanggilan mengambil parameter userState, yang dapat digunakan untuk melacak masa pakai setiap tugas. CancelAsync mengambil parameter userState, yang memungkinkan Anda membatalkan tugas tertentu yang tertunda.

Metode yang hanya mendukung satu operasi tertunda pada satu waktu, seperti Method1Async(string param), tidak dapat dibatalkan.

Menerima Pembaruan Kemajuan dan Hasil Bertambah Bertahap

Kelas yang mematuhi Pola Asinkron berbasis Peristiwa dapat menyediakan peristiwa secara opsional untuk melacak kemajuan dan hasil bertambah bertahap. Ini biasanya akan diberi nama ProgressChanged atau MethodNameProgressChanged, dan penanganan aktivitas yang sesuai akan mengambil parameter ProgressChangedEventArgs.

Penanganan aktivitas untuk peristiwa ProgressChanged dapat memeriksa properti ProgressChangedEventArgs.ProgressPercentage untuk menentukan persentase tugas asinkron apa yang telah diselesaikan. Properti ini akan berkisar dari 0 hingga 100, dan dapat digunakan untuk memperbarui properti Value dari ProgressBar. Jika beberapa operasi asinkron tertunda, Anda dapat menggunakan properti ProgressChangedEventArgs.UserState untuk membedakan operasi mana yang melaporkan kemajuan.

Beberapa kelas dapat melaporkan hasil bertambah bertahap saat operasi asinkron dilanjutkan. Hasil ini akan disimpan di kelas yang diturunkan dari ProgressChangedEventArgs dan hasilnya akan muncul sebagai properti di kelas turunan. Anda dapat mengakses hasil ini di penanganan aktivitas untuk peristiwa ProgressChanged tersebut, sama seperti Anda mengakses properti ProgressPercentage. Jika beberapa operasi asinkron tertunda, Anda dapat menggunakan properti UserState untuk membedakan operasi mana yang melaporkan hasil bertambah bertahap.

Lihat juga