Sebenarnya, async / menunggu tidaklah ajaib. Topik lengkapnya cukup luas tetapi untuk jawaban yang cepat namun cukup lengkap untuk pertanyaan Anda, saya pikir kami bisa mengaturnya.
Mari kita atasi acara klik tombol sederhana di aplikasi Windows Forms:
public async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before awaiting");
await GetSomethingAsync();
Console.WriteLine("after awaiting");
}
Saya akan secara eksplisit tidak berbicara tentang apa pun GetSomethingAsync
yang dikembalikan untuk saat ini. Anggap saja ini adalah sesuatu yang akan selesai setelah, katakanlah, 2 detik.
Dalam dunia tradisional, non-asinkron, dunia, pengendali event klik tombol Anda akan terlihat seperti ini:
public void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before waiting");
DoSomethingThatTakes2Seconds();
Console.WriteLine("after waiting");
}
Ketika Anda mengklik tombol dalam formulir, aplikasi akan muncul membeku sekitar 2 detik, sementara kami menunggu metode ini selesai. Apa yang terjadi adalah bahwa "pompa pesan", pada dasarnya sebuah loop, diblokir.
Lingkaran ini terus-menerus bertanya kepada windows "Apakah ada yang melakukan sesuatu, seperti menggerakkan mouse, mengklik sesuatu? Apakah saya perlu mengecat ulang sesuatu? Jika demikian, beri tahu saya!" dan kemudian memproses "sesuatu" itu. Loop ini mendapat pesan bahwa pengguna mengklik "button1" (atau jenis pesan yang setara dari Windows), dan akhirnya memanggil button1_Click
metode kami di atas. Sampai metode ini kembali, loop ini sekarang macet menunggu. Ini membutuhkan waktu 2 detik dan selama ini, tidak ada pesan yang sedang diproses.
Sebagian besar hal yang berhubungan dengan windows dilakukan dengan menggunakan pesan, yang berarti bahwa jika loop pesan berhenti memompa pesan, bahkan hanya sesaat, itu dengan cepat dapat dilihat oleh pengguna. Misalnya, jika Anda memindahkan notepad atau program lain di atas program Anda sendiri, dan kemudian pergi lagi, sejumlah pesan cat dikirim ke program Anda yang menunjukkan wilayah jendela yang sekarang tiba-tiba menjadi terlihat lagi. Jika loop pesan yang memproses pesan-pesan ini sedang menunggu sesuatu, diblokir, maka tidak ada lukisan yang dilakukan.
Jadi, jika dalam contoh pertama, async/await
tidak membuat utas baru, bagaimana cara melakukannya?
Nah, yang terjadi adalah metode Anda dibagi menjadi dua. Ini adalah salah satu dari jenis topik yang luas sehingga saya tidak akan membahas terlalu banyak detail tetapi cukup untuk mengatakan metode ini dibagi menjadi dua hal ini:
- Semua kode mengarah ke
await
, termasuk panggilan keGetSomethingAsync
- Semua kode berikut
await
Ilustrasi:
code... code... code... await X(); ... code... code... code...
Ulang:
code... code... code... var x = X(); await X; code... code... code...
^ ^ ^ ^
+---- portion 1 -------------------+ +---- portion 2 ------+
Pada dasarnya metode dijalankan seperti ini:
- Itu mengeksekusi semuanya hingga
await
Itu memanggil GetSomethingAsync
metode, yang melakukan hal itu, dan mengembalikan sesuatu yang akan menyelesaikan 2 detik di masa depan
Sejauh ini kita masih di dalam panggilan asli ke button1_Click, terjadi di utas utama, dipanggil dari loop pesan. Jika pembuatan kode await
membutuhkan banyak waktu, UI akan tetap membeku. Dalam contoh kita, tidak banyak
Apa await
kata kunci, bersama dengan beberapa sihir kompiler pintar, apakah itu pada dasarnya sesuatu seperti "Oke, Anda tahu apa, saya hanya akan kembali dari pengendali event klik tombol di sini. Ketika Anda (seperti, hal yang kami sedang menunggu) menyiasati untuk menyelesaikan, beri tahu saya karena saya masih memiliki beberapa kode tersisa untuk dieksekusi ".
Sebenarnya itu akan membiarkan kelas SynchronizationContext tahu bahwa itu dilakukan, yang, tergantung pada konteks sinkronisasi aktual yang sedang dimainkan saat ini, akan mengantri untuk dieksekusi. Kelas konteks yang digunakan dalam program Windows Forms akan mengantri menggunakan antrian yang dipompa oleh loop pesan.
Jadi ia kembali ke loop pesan, yang sekarang bebas untuk terus memompa pesan, seperti memindahkan jendela, mengubah ukurannya, atau mengklik tombol lain.
Untuk pengguna, UI sekarang responsif lagi, memproses klik tombol lainnya, mengubah ukuran dan yang paling penting, menggambar ulang , sehingga tampaknya tidak membeku.
- 2 detik kemudian, hal yang kita tunggu selesaikan dan apa yang terjadi sekarang adalah bahwa itu (well, konteks sinkronisasi) menempatkan pesan ke dalam antrian yang dilihat oleh loop pesan, mengatakan "Hei, saya punya beberapa kode lagi untuk Anda mengeksekusi ", dan kode ini adalah semua kode setelah menunggu.
- Ketika loop pesan sampai ke pesan itu, pada dasarnya ia akan "memasukkan kembali" metode yang ditinggalkannya, tepat setelah
await
dan terus menjalankan sisa metode. Perhatikan bahwa kode ini sekali lagi dipanggil dari loop pesan sehingga jika kode ini melakukan sesuatu yang panjang tanpa menggunakan async/await
dengan benar, itu lagi akan memblokir loop pesan
Ada banyak bagian yang bergerak di bawah tenda di sini jadi di sini ada beberapa tautan ke informasi lebih lanjut, saya akan mengatakan "jika Anda membutuhkannya", tetapi topik ini cukup luas dan cukup penting untuk mengetahui beberapa bagian yang bergerak itu . Selalu Anda akan mengerti bahwa async / menunggu masih konsep yang bocor. Beberapa batasan dan masalah mendasar masih bocor ke kode di sekitarnya, dan jika tidak, Anda biasanya harus men-debug aplikasi yang rusak secara acak karena tampaknya tidak ada alasan yang bagus.
OK, jadi bagaimana jika GetSomethingAsync
memutar utas yang akan selesai dalam 2 detik? Ya, maka jelas ada utas baru dalam permainan. Namun, utas ini bukan karena async-ness metode ini, melainkan karena pemrogram metode ini memilih utas untuk mengimplementasikan kode asinkron. Hampir semua I / O tidak sinkron tidak menggunakan utas, mereka menggunakan hal-hal yang berbeda. async/await
sendiri tidak memunculkan thread baru tapi jelas "hal-hal yang kita tunggu" dapat diimplementasikan menggunakan thread.
Ada banyak hal di .NET yang tidak selalu memutar utas sendiri tetapi masih asinkron:
- Permintaan web (dan banyak hal terkait jaringan lainnya yang membutuhkan waktu)
- Membaca dan menulis file tidak sinkron
- dan banyak lagi, pertanda baik adalah jika kelas / antarmuka yang dimaksud memiliki metode bernama
SomethingSomethingAsync
atau BeginSomething
dan EndSomething
dan ada yang IAsyncResult
terlibat.
Biasanya hal-hal ini tidak menggunakan utas di bawah tenda.
OK, jadi Anda ingin beberapa "topik luas"?
Baiklah, mari kita tanyakan pada Try Roslyn tentang klik tombol kita:
Coba Roslyn
Saya tidak akan menautkan kelas yang dibuat penuh di sini, tetapi ini adalah hal yang sangat mengerikan.