Bagikan melalui


Model pemrograman tugas asinkron

Anda dapat menghindari penyempitan performa dan meningkatkan respons keseluruhan aplikasi Anda dengan menggunakan pemrograman asinkron. Namun, teknik tradisional untuk menulis aplikasi asinkron bisa rumit, membuatnya sulit ditulis, di-debug, dan dipertahankan.

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. Akibatnya, Anda mendapatkan semua keuntungan dari pemrograman asinkron dengan sebagian kecil dari upaya.

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 seperti itu diblokir dalam proses sinkron, seluruh aplikasi harus menunggu. Dalam proses asinkron, aplikasi dapat dilanjutkan dengan pekerjaan lain yang tidak bergantung pada sumber daya web sampai tugas yang berpotensi memblokir selesai.

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

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

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

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

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

Metode asinkron mudah ditulis

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

Contoh berikut menunjukkan metode asinkron. Hampir semua yang ada dalam kode akan terlihat akrab untuk Anda.

Anda dapat menemukan contoh Windows Presentation Foundation (WPF) lengkap yang tersedia untuk diunduh dari pemrograman asinkron dengan async dan await dalam 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 sampel sebelumnya. Mulailah dengan definisi metode. Ini termasuk pengubah async . Jenis pengembalian adalah Task<int> (Lihat bagian "Jenis Pengembalian" untuk opsi lainnya). Nama metode berakhiran Async. Dalam isi metode , GetStringAsync mengembalikan Task<string>. Itu berarti bahwa ketika Anda await tugas Anda akan mendapatkan string (contents). Sebelum menunggu tugas, Anda dapat melakukan pekerjaan yang tidak bergantung pada string yang berasal dari GetStringAsync.

Perhatikan dengan seksama operator await. Ini menangguhkan GetUrlContentLengthAsync:

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

Pernyataan pengembalian menentukan hasil bilangan bulat. Metode apa pun yang sedang menunggu GetUrlContentLengthAsync akan mengambil nilai panjang.

Jika GetUrlContentLengthAsync tidak memiliki pekerjaan apa pun 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 meringkas apa yang membuat contoh sebelumnya sebagai metode asinkron:

  • Definisi metode mencakup pengubah async.

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

  • Jenis pengembalian adalah salah satu jenis berikut:

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

    Untuk informasi selengkapnya, lihat bagian Tipe pengembalian dan parameter.

  • Metode ini biasanya mencakup setidaknya satu await ekspresi, yang menandai titik di mana metode tidak dapat dilanjutkan sampai operasi asinkron yang ditunggu selesai. Sementara itu, metode ditangguhkan, dan kontrol kembali ke pemanggil metode. Bagian berikutnya 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 pengkompilasi 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, bisa 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.

Apa yang terjadi dalam metode asinkron

Hal terpenting yang perlu dipahami dalam pemrograman asinkron adalah bagaimana alur kontrol berpindah dari metode ke metode. Diagram berikut mengarahkan Anda melalui proses:

Melacak navigasi alur kontrol asinkron

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

  1. Metode pemanggilan memanggil dan menunggu metode GetUrlContentLengthAsync asinkron.

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

  3. Sesuatu terjadi dalam GetStringAsync yang menangguhkan kemajuannya. Mungkin harus menunggu pengunduhan situs web atau aktivitas penghalang lainnya. Untuk menghindari pemblokiran sumber daya, GetStringAsync menghasilkan kontrol kepada pemanggilnya, GetUrlContentLengthAsync.

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

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

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

  6. GetUrlContentLengthAsync telah kehabisan pekerjaan yang dapat dilakukannya tanpa hasil dari getStringTask. GetUrlContentLengthAsync selanjutnya 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 eksekusinya dan mengalihkan kontrol ke metode yang memanggil GetUrlContentLengthAsync. GetUrlContentLengthAsync mengembalikan Task<int> ke pemanggilnya. Tugas ini mewakili janji untuk menghasilkan hasil berupa bilangan bulat yang menunjukkan panjang string yang telah diunduh.

    Nota

    Jika GetStringAsync (dan oleh karena itu getStringTask) selesai sebelum GetUrlContentLengthAsync menunggunya, kontrol tetap berada di GetUrlContentLengthAsync. Biaya menangguhkan dan kemudian kembali ke GetUrlContentLengthAsync akan terbuang jika proses asinkron yang disebut getStringTask telah selesai dan GetUrlContentLengthAsync tidak perlu menunggu hasil akhir.

    Dalam metode pemanggilan, pola pemrosesan terus berlanjut. Pemanggil mungkin melakukan pekerjaan lain yang tidak bergantung pada hasil dari GetUrlContentLengthAsync sebelum menanti hasil tersebut, atau pemanggil mungkin langsung menanti. Metode panggilan menunggu GetUrlContentLengthAsync, dan GetUrlContentLengthAsync sedang menunggu GetStringAsync.

  7. GetStringAsync menyelesaikan dan menghasilkan hasil string. Hasil string tidak dikembalikan oleh panggilan ke GetStringAsync dengan cara yang mungkin Anda harapkan. (Ingat bahwa metode sudah mengembalikan tugas di langkah 3.) Sebaliknya, hasil string disimpan dalam tugas yang mewakili penyelesaian metode , getStringTask. Operator tunggu mengambil hasil dari getStringTask. Pernyataan penugasan 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 menggunakan pemrograman asinkron, luangkan waktu satu menit untuk mempertimbangkan perbedaan antara perilaku sinkron dan asinkron. Metode sinkron kembali 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 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 "Async" yang ditambahkan ke nama anggota, dan jenis pengembaliannya adalah Task atau Task<TResult>. Misalnya, System.IO.Stream kelas berisi metode seperti CopyToAsync, , ReadAsyncdan WriteAsync bersama metode CopyTosinkron , , Readdan Write.

Windows Runtime 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 Mulai Cepat: Memanggil API asinkron di C# atau Visual Basic jika Anda menggunakan versi Windows Runtime yang lebih lama.

Utsan

Metode asinkron dimaksudkan sebagai operasi yang tidak memblokir. Ekspresi await dalam metode asinkron tidak memblokir utas saat ini saat tugas yang ditunggu sedang berjalan. Sebagai gantinya, ekspresi mendaftarkan sisa metode sebagai kelanjutan dan mengembalikan kontrol ke pemanggil metode asinkron.

async dan await tidak menyebabkan utas tambahan dibuat. Metode asinkron tidak memerlukan multithreading karena metode asinkron tidak berjalan pada utasnya sendiri. Metode ini berjalan di konteks sinkronisasi saat ini dan memanfaatkan 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 agar tersedia.

Pendekatan berbasis asinkron untuk pemrograman asinkron lebih disukai dibandingkan pendekatan yang ada dalam hampir setiap kasus. Secara khusus, pendekatan ini lebih baik daripada kelas BackgroundWorker untuk operasi yang mengikat I/O karena kodenya lebih sederhana dan Anda tidak perlu khawatir tentang kondisi balapan. Dalam kombinasi dengan metode Task.Run, pemrograman asinkron lebih tepat daripada BackgroundWorker untuk operasi terikat CPU karena pemrograman asinkron memisahkan koordinasi menjalankan kode Anda dari pekerjaan yang dialihkan Task.Run ke kumpulan utas.

asinkron dan menunggu

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

  • Metode asinkron yang ditandai dapat menggunakan tunggu untuk menunjuk titik penangguhan. Operator await memberi tahu pengompilasi bahwa metode asinkron tidak dapat melanjutkan melewati titik itu sampai proses asinkron yang ditunggu selesai. Sementara itu, pengendalian kembali ke pemanggil metode asinkron.

    Penangguhan sementara metode asinkron pada ekspresi await tidak berarti keluar dari metode, dan blok kode finally tidak dijalankan.

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

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

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

Jenis dan parameter pengembalian

Metode asinkron biasanya mengembalikan Task atau Task<TResult>. Di dalam metode async, operator await diterapkan pada task yang dikembalikan dari pemanggilan metode async lainnya.

Anda menentukan Task<TResult> sebagai jenis pengembalian jika metode berisi return pernyataan yang menentukan 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 metode GetAwaiter. ValueTask<TResult> adalah contoh jenis seperti itu. Ini tersedia dalam paket System.Threading.Tasks.Extension NuGet.

Contoh berikut menunjukkan cara 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, memberikan hasil akhir dari proses atau pengecualian yang terjadi jika proses tidak berhasil.

Metode asinkron juga dapat memiliki void jenis pengembalian. Jenis pengembalian ini digunakan terutama untuk menentukan penanganan aktivitas, di mana void jenis pengembalian 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 yang mengembalikan void tidak dapat menangkap pengecualian apa pun yang dilemparkan oleh metode tersebut.

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

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

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

Konvensi penamaan

Menurut konvensi, metode yang mengembalikan jenis yang biasanya ditunggu (misalnya, Task, Task<T>, ValueTask, ValueTask<T>) harus memiliki nama yang diakhiri dengan "Async". Metode yang memulai operasi asinkron tetapi tidak mengembalikan jenis yang dapat ditunggu seharusnya tidak memiliki nama yang diakhir dengan "Asinkron", tetapi dapat dimulai dengan "Begin", "Start", atau beberapa kata kerja lainnya untuk menyarankan metode ini tidak mengembalikan atau melempar hasil operasi.

Anda dapat mengabaikan konvensi di mana 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#) Menunjukkan cara memulai beberapa tugas secara bersamaan.
Jenis pengembalian asinkron (C#) Mengilustrasikan jenis yang dapat dikembalikan oleh metode asinkron, dan menjelaskan kapan setiap jenis sesuai.
Batalkan tugas dengan token pembatalan sebagai mekanisme sinyal. Memperlihatkan cara menambahkan fungsionalitas berikut ke solusi asinkron Anda:

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

Lihat juga