Bagikan melalui


Model pemrograman tugas asinkron

Anda dapat menghindari kemacetan perulangan dan meningkatkan respons keseluruhan aplikasi dengan menggunakan pemrograman asinkron. Namun, teknik yang biasa digunakan untuk menulis aplikasi asinkron dapat menjadi rumit, membuatnya sulit untuk ditulis, di-debug, dan dipelihara.

C# mendukung pendekatan yang disederhanakan, pemrograman asinkron, yang memanfaatkan dukungan asinkron dalam runtime .NET. Pengompilasi melakukan pekerjaan sulit yang biasa dilakukan pengembang, dan aplikasi Anda mempertahankan struktur logis yang menyerupai kode sinkron. Hasilnya, Anda mendapatkan semua keuntungan dari pemrograman asinkron dengan sedikit usaha.

Topik ini memberikan gambaran umum tentang kapan dan bagaimana menggunakan pemrograman asinkron dan menyertakan tautan ke topik dukungan yang berisi detail dan contoh.

Asinkron meningkatkan responsivitas

Asinkron sangat penting untuk aktivitas yang berpotensi memblokir, seperti akses web. Akses ke sumber daya web terkadang lambat atau tertunda. Jika aktivitas tersebut diblokir dalam proses sinkron, seluruh aplikasi harus menunggu. Dalam proses asinkron, aplikasi dapat melanjutkan pekerjaan lain yang tidak bergantung pada sumber daya web hingga tugas yang berpotensi memblokir selesai.

Tabel berikut menunjukkan area umum di mana pemrograman asinkron meningkatkan responsivitas. API yang terdaftar dari .NET dan Runtime Windows berisi metode yang mendukung pemrograman asinkron.

Area aplikasi Jenis .NET dengan metode asinkron Jenis Runtime Windows dengan metode asinkron
Akses web HttpClient Windows.Web.Http.HttpClient
SyndicationClient
Bekerja dengan file JsonSerializer
StreamReader
StreamWriter
XmlReader
XmlWriter
StorageFile
Bekerja dengan gambar MediaCapture
BitmapEncoder
BitmapDecoder
Pemrograman WCF Operasi Sinkron dan Asinkron

Asinkron terbukti sangat berharga untuk aplikasi yang mengakses utas UI karena semua aktivitas terkait UI biasanya berbagi satu utas. Jika ada proses yang diblokir dalam aplikasi sinkron, semua proses juga akan diblokir. Aplikasi Anda berhenti merespons, dan Anda mungkin menyimpulkan bahwa aplikasi tersebut gagal padahal hanya menunggu.

Saat Anda menggunakan metode asinkron, aplikasi akan terus merespons UI. Anda dapat mengubah ukuran atau meminimalkan jendela, misalnya, atau Anda dapat menutup aplikasi jika Anda tidak ingin menunggu sampai selesai.

Pendekatan berbasis asinkron menambahkan yang setara dengan transmisi otomatis ke daftar opsi yang dapat Anda pilih saat merancang operasi asinkron. Artinya, Anda mendapatkan semua manfaat dari pemrograman asinkron tradisional tetapi dengan usaha yang jauh lebih sedikit dari pengembang.

Metode asinkron mudah ditulis

Kata kunci asinkron dan menunggu dalam C# adalah inti dari pemrograman asinkron. Dengan menggunakan dua kata kunci tersebut, Anda dapat menggunakan sumber daya di .NET Framework, .NET Core, atau Runtime Windows untuk membuat metode asinkron hampir semudah saat Anda membuat metode sinkron. Metode asinkron yang Anda tetapkan dengan menggunakan kata kunci async disebut sebagai metode asinkron.

Contoh berikut menunjukkan metode asinkron. Hampir semua yang ada dalam kode akan terlihat familier bagi Anda.

Anda dapat menemukan contoh Windows Presentation Foundation (WPF) lengkap yang dapat diunduh dari Pemrograman asinkron dengan asinkron dan menunggu di C#.

public async Task<int> GetUrlContentLengthAsync()
{
    using var client = new HttpClient();

    Task<string> getStringTask =
        client.GetStringAsync("https://learn.microsoft.com/dotnet");

    DoIndependentWork();

    string contents = await getStringTask;

    return contents.Length;
}

void DoIndependentWork()
{
    Console.WriteLine("Working...");
}

Anda dapat mempelajari beberapa praktik dari contoh sebelumnya. Mulailah dengan tanda tangan metode. Ini termasuk pengubah async. Jenis pengembaliannya adalah Task<int> (Lihat bagian "Jenis Pengembalian" untuk opsi lainnya). Nama metode diakhiri dengan Async. Dalam isi metode, GetStringAsync mengembalikan Task<string>. Itu berarti ketika Anda await tugas, Anda akan mendapatkan string (contents). Sebelum menunggu tugas, Anda dapat melakukan pekerjaan yang tidak bergantung pada string dari GetStringAsync.

Perhatikan baik-baik operator await. Ini menangguhkan GetUrlContentLengthAsync:

  • GetUrlContentLengthAsync tidak dapat dilanjutkan hingga getStringTask selesai.
  • Sementara itu, kontrol kembali ke pemanggil GetUrlContentLengthAsync.
  • Kontrol dilanjutkan di sini saat getStringTask selesai.
  • Operator await kemudian mengambil hasil string dari getStringTask.

Pernyataan kembali menentukan hasil bilangan bulat. Metode apa pun yang menunggu GetUrlContentLengthAsync mengambil nilai panjangnya.

Jika GetUrlContentLengthAsync tidak memiliki pekerjaan yang dapat dilakukan antara memanggil GetStringAsync dan menunggu penyelesaiannya, Anda dapat menyederhanakan kode Anda dengan memanggil dan menunggu dalam satu pernyataan berikut.

string contents = await client.GetStringAsync("https://learn.microsoft.com/dotnet");

Karakteristik berikut merangkum apa yang menjadikan contoh sebelumnya sebagai metode asinkron:

  • Tanda tangan metode menyertakan pengubah async.

  • Nama metode asinkron, menurut konvensi, diakhiri dengan akhiran "Asinkron".

  • Jenis pengembaliannya adalah salah satu dari jenis berikut:

    • Task<TResult> jika metode Anda memiliki pernyataan pengembalian di mana operand memiliki jenis TResult.
    • Task jika metode Anda tidak memiliki pernyataan kembali atau memiliki pernyataan kembali tanpa operand.
    • void jika Anda sedang menulis penanganan aktivitas asinkron.
    • Jenis lain yang memiliki GetAwaiter metode .

    Untuk informasi selengkapnya, lihat bagian Jenis dan parameter pengembalian.

  • Metode ini biasanya menyertakan setidaknya satu ekspresi await, yang menandai titik di mana metode tidak dapat melanjutkan hingga operasi asinkron yang ditunggu selesai. Sementara itu, metode ditangguhkan, dan kontrol kembali ke pemanggil metode. Bagian selanjutnya dari topik ini menggambarkan apa yang terjadi pada titik penangguhan.

Dalam metode asinkron, Anda menggunakan kata kunci dan jenis yang disediakan untuk menunjukkan apa yang ingin Anda lakukan, dan pengompilasi melakukan sisanya, termasuk melacak apa yang harus terjadi ketika kontrol kembali ke titik tunggu dalam metode yang ditangguhkan. Beberapa proses rutin, seperti perulangan dan penanganan pengecualian, mungkin sulit ditangani dalam kode asinkron tradisional. Dalam metode asinkron, Anda menulis elemen-elemen ini sebanyak yang Anda lakukan dalam solusi sinkron, dan masalahnya diselesaikan.

Untuk informasi selengkapnya tentang asinkron di versi .NET Framework sebelumnya, lihat TPL dan pemrograman asinkron .NET Framework tradisional .

Yang terjadi dalam metode asinkron

Hal yang paling penting untuk dipahami dalam pemrograman asinkron adalah bagaimana alur kontrol berpindah dari satu metode ke metode yang lain. Diagram berikut akan memandu Anda melalui proses:

Trace navigation of async control flow

Angka-angka dalam diagram sesuai dengan langkah-langkah berikut, dimulai saat metode panggilan memanggil metode asinkron.

  1. Metode panggilan memanggil dan menunggu metode asinkron GetUrlContentLengthAsync.

  2. GetUrlContentLengthAsync membuat instans HttpClient dan memanggil metode asinkron GetStringAsync untuk mengunduh konten situs web sebagai string.

  3. Sesuatu terjadi di GetStringAsync yang menunda kemajuannya. Mungkin harus menunggu situs web mengunduh atau aktivitas pemblokiran lainnya. Untuk menghindari pemblokiran sumber daya, GetStringAsync memberikan kontrol kepada pemanggilnya, GetUrlContentLengthAsync.

    GetStringAsync mengembalikan Task<TResult>, di mana TResult adalah string, dan GetUrlContentLengthAsync menetapkan tugas ke variabel getStringTask. Tugas mewakili proses yang sedang berlangsung untuk panggilan ke GetStringAsync, dengan komitmen untuk menghasilkan nilai string aktual saat pekerjaan selesai.

  4. Karena getStringTask belum ditunggu, GetUrlContentLengthAsync dapat melanjutkan pekerjaan lain yang tidak bergantung pada hasil akhir dari GetStringAsync. Pekerjaan tersebut diwakili oleh panggilan ke metode sinkron DoIndependentWork.

  5. DoIndependentWork adalah metode sinkron yang melakukan tugasnya dan kembali ke pemanggilnya.

  6. GetUrlContentLengthAsync telah kehabisan pekerjaan yang dapat dilakukan tanpa hasil dari getStringTask. GetUrlContentLengthAsync berikutnya ingin menghitung dan mengembalikan panjang string yang diunduh, tetapi metode tidak dapat menghitung nilai tersebut sampai metode memiliki string.

    Oleh karena itu, GetUrlContentLengthAsync menggunakan operator tunggu untuk menangguhkan kemajuannya dan menghasilkan kontrol ke metode yang disebut GetUrlContentLengthAsync. GetUrlContentLengthAsync mengembalikan Task<int> ke pemanggil. Tugas ini mewakili janji untuk menghasilkan hasil bilangan bulat yang merupakan panjang dari string yang diunduh.

    Catatan

    Jika GetStringAsync (dan oleh karena itu getStringTask) selesai sebelum GetUrlContentLengthAsync menunggunya, kontrol tetap berada di GetUrlContentLengthAsync. Biaya penangguhan dan kemudian kembali ke GetUrlContentLengthAsync akan sia-sia jika proses asinkron yang dipanggil getStringTask telah selesai dan GetUrlContentLengthAsync tidak harus menunggu hasil akhir.

    Di dalam metode panggilan, pola pemrosesan berlanjut. Pemanggil mungkin melakukan pekerjaan lain yang tidak bergantung pada hasil dari GetUrlContentLengthAsync sebelum menunggu hasil tersebut, atau pemanggil mungkin segera menunggu. Metode panggilan sedang menunggu GetUrlContentLengthAsync, dan GetUrlContentLengthAsync sedang menunggu GetStringAsync.

  7. GetStringAsync menyelesaikan dan menghasilkan hasil string. Hasil string tidak dikembalikan oleh panggilan ke GetStringAsync seperti yang Anda harapkan. (Ingat bahwa metode sudah mengembalikan tugas di langkah 3.) Sebagai gantinya, hasil string disimpan dalam tugas yang mewakili penyelesaian metode, getStringTask. Operator menunggu mengambil hasil dari getStringTask. Pernyataan penetapan menetapkan hasil yang diambil ke contents.

  8. Ketika GetUrlContentLengthAsync memiliki hasil string, metode dapat menghitung panjang string. Kemudian pekerjaan GetUrlContentLengthAsync juga selesai, dan penanganan aktivitas yang menunggu dapat dilanjutkan. Dalam contoh lengkap di akhir topik, Anda dapat mengonfirmasi bahwa penanganan aktivitas mengambil dan mencetak nilai hasil panjang. Jika Anda baru mengenal pemrograman asinkron, luangkan waktu sejenak untuk mempertimbangkan perbedaan antara perilaku sinkron dan asinkron. Metode sinkron mengembalikan ketika pekerjaannya selesai (langkah 5), tetapi metode asinkron mengembalikan nilai tugas ketika pekerjaannya ditangguhkan (langkah 3 dan 6). Ketika metode asinkron akhirnya menyelesaikan pekerjaannya, tugas ditandai sebagai selesai dan hasilnya, jika ada, disimpan dalam tugas.

Metode asinkron API

Anda mungkin bertanya-tanya di mana bisa menemukan metode seperti GetStringAsync yang mendukung pemrograman asinkron. .NET Framework 4.5 atau yang lebih tinggi dan .NET Core berisi banyak anggota yang bekerja dengan async dan await. Anda dapat mengenalinya dengan akhiran "Asinkron" yang ditambahkan ke nama anggota, dan dengan jenis pengembalian Task atau Task<TResult>. Misalnya, kelas System.IO.Stream berisi metode seperti CopyToAsync, ReadAsync, dan WriteAsync di samping metode sinkron CopyTo, Read, dan Write.

Runtime Windows juga berisi banyak metode yang dapat Anda gunakan dengan async dan await di aplikasi Windows. Untuk informasi selengkapnya, lihat Pemrograman utas dan asinkron untuk pengembangan UWP, dan Pemrograman asinkron (aplikasi Windows Store) dan Panduan Memulai: Memanggil API asinkron dalam C# atau Visual Basic jika Anda menggunakan versi Runtime Windows sebelumnya.

Utas

Metode asinkron dimaksudkan untuk menjadi operasi non-pemblokiran. Ekspresi await dalam metode asinkron tidak memblokir utas saat ini ketika tugas yang ditunggu sedang berjalan. Sebaliknya, ekspresi mendaftarkan sisa metode sebagai kelanjutan dan mengembalikan kontrol ke pemanggil metode asinkron.

Kata kunci async dan await tidak menyebabkan utas tambahan dibuat. Metode asinkron tidak memerlukan multi-utas karena metode asinkron tidak berjalan pada utasnya sendiri. Metode ini berjalan pada konteks sinkronisasi saat ini dan menggunakan waktu pada utas hanya ketika metode aktif. Anda dapat menggunakan Task.Run untuk memindahkan pekerjaan yang terikat CPU ke utas latar belakang, tetapi utas latar belakang tidak membantu proses yang hanya menunggu hasil tersedia.

Pendekatan berbasis asinkron untuk pemrograman asinkron lebih disukai untuk pendekatan yang ada di hampir setiap kasus. Secara khusus, pendekatan ini lebih baik daripada kelas BackgroundWorker untuk operasi terikat I/O karena kodenya lebih sederhana dan Anda tidak perlu menjaga kondisi balapan. Dalam kombinasi dengan Task.Run, pemrograman asinkron lebih baik daripada BackgroundWorker untuk operasi terikat CPU karena pemrograman asinkron memisahkan detail koordinasi menjalankan kode Anda dari pekerjaan yang ditransfer Task.Run ke threadpool.

asinkron dan menunggu

Jika Anda menetapkan bahwa suatu metode adalah metode asinkron dengan menggunakan pengubah asinkron, Anda mengaktifkan dua kemampuan berikut.

  • Metode asinkron yang ditandai dapat menggunakan menunggu untuk menetapkan titik penangguhan. Operator await memberi tahu pengompilasi bahwa metode asinkron tidak dapat melanjutkan melewati titik itu hingga proses asinkron yang ditunggu selesai. Sementara itu, kontrol kembali ke pemanggil metode asinkron.

    Penangguhan metode asinkron pada ekspresi await bukan merupakan jalan keluar dari metode, dan finally blok tidak berjalan.

  • Metode asinkron yang ditandai itu sendiri dapat ditunggu oleh metode yang memanggilnya.

Metode asinkron biasanya berisi satu atau beberapa kemunculan operator await, tetapi tidak adanya ekspresi await tidak menyebabkan kesalahan pengompilasi. Jika metode asinkron tidak menggunakan operator await untuk menandai titik penangguhan, metode akan dijalankan seperti metode sinkron, terlepas dari pengubah async. Pengompilasi mengeluarkan peringatan untuk metode tersebut.

async dan await adalah kata kunci kontekstual. Untuk contoh dan informasi selengkapnya, lihat topik berikut:

Mengembalikan jenis dan parameter

Metode asinkron biasanya mengembalikan Task atau Task<TResult>. Di dalam metode asinkron, operator await diterapkan ke tugas yang dikembalikan dari panggilan ke metode asinkron lainnya.

Anda menetapkan Task<TResult> sebagai jenis pengembalian jika metode berisi pernyataan return yang menetapkan operand jenis TResult.

Anda menggunakan Task sebagai jenis pengembalian jika metode tidak memiliki pernyataan pengembalian atau memiliki pernyataan pengembalian yang tidak mengembalikan operand.

Anda juga dapat menentukan jenis pengembalian lainnya, asalkan jenis menyertakan GetAwaiter metode . ValueTask<TResult> adalah contoh dari jenis tersebut. Ini tersedia dalam paket NuGet System.Threading.Tasks.Extension.

Contoh berikut menunjukkan bagaimana Anda mendeklarasikan dan memanggil metode yang mengembalikan Task<TResult> atau Task:

async Task<int> GetTaskOfTResultAsync()
{
    int hours = 0;
    await Task.Delay(0);

    return hours;
}


Task<int> returnedTaskTResult = GetTaskOfTResultAsync();
int intResult = await returnedTaskTResult;
// Single line
// int intResult = await GetTaskOfTResultAsync();

async Task GetTaskAsync()
{
    await Task.Delay(0);
    // No return statement needed
}

Task returnedTask = GetTaskAsync();
await returnedTask;
// Single line
await GetTaskAsync();

Setiap tugas yang dikembalikan mewakili pekerjaan yang sedang berlangsung. Tugas merangkum informasi tentang status proses asinkron dan, akhirnya, baik hasil akhir dari proses atau pengecualian yang dimunculkan proses jika tidak berhasil.

Metode asinkron juga dapat memiliki jenis pengembalian void. Jenis pengembalian ini digunakan terutama untuk mendefinisikan pengendali peristiwa, di mana jenis pengembalian void diperlukan. Penanganan aktivitas asinkron sering berfungsi sebagai titik awal untuk program asinkron.

Metode asinkron yang memiliki jenis pengembalian void tidak dapat ditunggu, dan pemanggil metode pengembalian void tidak dapat menangkap pengecualian apa pun yang ditampilkan metode tersebut.

Metode asinkron tidak dapat mendeklarasikan parameter in, ref atau out, tetapi metode tersebut dapat memanggil metode yang memiliki parameter tersebut. Demikian pula, metode asinkron tidak dapat mengembalikan nilai dengan referensi, meskipun metode ini dapat memanggil metode dengan nilai pengembalian referensi.

Untuk informasi dan contoh selengkapnya, lihat Jenis pengembalian asinkron (C#).

API asinkron dalam pemrograman Runtime Windows memiliki salah satu jenis pengembalian berikut, yang mirip dengan tugas:

Konvensi penamaan

Berdasarkan konvensi, metode yang mengembalikan jenis yang biasanya dapat ditunggu (misalnya, Task, Task<T>, ValueTask, ValueTask<T>) harus memiliki nama yang diakhiri dengan "Asinkron". Metode yang memulai operasi asinkron tetapi tidak mengembalikan jenis yang dapat ditunggu tidak boleh memiliki nama yang diakhiri dengan "Asinkron", tetapi dapat dimulai dengan "Mulai", "Mulai", atau kata kerja lain yang menyarankan metode ini tidak mengembalikan atau menampilkan hasil operasi.

Anda dapat mengabaikan konvensi di mana suatu peristiwa, kelas dasar, atau kontrak antarmuka menyarankan nama yang berbeda. Misalnya, Anda tidak boleh mengganti nama penanganan aktivitas umum, seperti OnButtonClick.

Artikel terkait (Visual Studio)

Judul Deskripsi
Cara membuat beberapa permintaan web secara paralel dengan menggunakan asinkron dan menunggu (C#) Mendemonstrasikan cara memulai beberapa tugas secara bersamaan.
Jenis pengembalian asinkron (C#) Mengilustrasikan jenis yang dapat dikembalikan oleh metode asinkron, dan menjelaskan kapan setiap jenis sesuai.
Membatalkan tugas dengan token pembatalan sebagai mekanisme sinyal. Menunjukkan cara menambahkan fungsionalitas berikut ke solusi asinkron Anda:

- Membatalkan daftar tugas (C#)
- Membatalkan tugas setelah jangka waktu tertentu (C#)
- Memproses tugas asinkron saat selesai (C#)
Menggunakan asinkron untuk akses file (C#) Mencantumkan dan mendemonstrasikan manfaat menggunakan asinkron dan menunggu untuk mengakses file.
Pola asinkron berbasis tugas (TAP) Menjelaskan pola asinkron, pola didasarkan pada jenis Task dan Task<TResult>.
Video Asinkron di Channel 9 Menyediakan tautan ke berbagai video tentang pemrograman asinkron.

Lihat juga