Apakah ini jenis hibrida? (misalnya, apakah program .NET saya menggunakan stack sampai hit async call kemudian beralih ke beberapa struktur lain sampai selesai, di mana stack dibatalkan kembali ke keadaan di mana ia dapat memastikan item berikutnya, dll? )
Pada dasarnya ya.
Misalkan kita punya
async void MyButton_OnClick() { await Foo(); Bar(); }
async Task Foo() { await Task.Delay(123); Blah(); }
Berikut adalah penjelasan yang sangat disederhanakan tentang bagaimana kelanjutannya diverifikasi. Kode yang sebenarnya jauh lebih kompleks, tetapi hal ini membuat gagasan tersebut melintas.
Anda mengklik tombol. Sebuah pesan diantrekan. Putaran pesan memproses pesan dan memanggil penangan klik, meletakkan alamat pengirim antrian pesan di tumpukan. Artinya, hal yang terjadi setelah handler selesai adalah bahwa loop pesan harus tetap berjalan. Jadi kelanjutan dari handler adalah loop.
Pawang klik memanggil Foo (), meletakkan alamat pengirim itu sendiri di tumpukan. Artinya, kelanjutan dari Foo adalah sisa dari pengendali klik.
Foo memanggil Task.Delay, meletakkan alamat pengirim itu sendiri di stack.
Task.Delay melakukan sihir apa pun yang perlu dilakukan untuk segera mengembalikan Tugas. Tumpukan muncul dan kami kembali ke Foo.
Foo memeriksa tugas yang dikembalikan untuk melihat apakah sudah selesai. Bukan itu. The kelanjutan dari Tunggulah, adalah untuk memanggil Blah (), sehingga Foo menciptakan delegasi yang panggilan Blah (), dan tanda-tanda yang mendelegasikan sebagai kelanjutan dari tugas. (Saya baru saja membuat pernyataan yang keliru; apakah Anda menangkapnya? Jika tidak, kami akan mengungkapkannya sebentar lagi.)
Foo kemudian membuat objek Tugasnya sendiri, menandainya sebagai tidak lengkap, dan mengembalikannya ke penangan klik.
Handler klik memeriksa tugas Foo dan menemukan itu tidak lengkap. Kelanjutan dari menunggu dalam handler adalah memanggil Bar (), jadi handler klik membuat delegasi yang memanggil Bar () dan menetapkannya sebagai kelanjutan dari tugas yang dikembalikan oleh Foo (). Kemudian mengembalikan tumpukan ke loop pesan.
Lingkaran pesan terus memproses pesan. Akhirnya sihir timer yang dibuat oleh tugas penundaan melakukan tugasnya dan memposting pesan ke antrian yang mengatakan bahwa kelanjutan tugas penundaan sekarang dapat dieksekusi. Jadi loop pesan memanggil kelanjutan tugas, menempatkan dirinya di tumpukan seperti biasa. Delegasi itu memanggil Blah (). Blah () melakukan apa yang dilakukannya dan mengembalikan tumpukan.
Sekarang apa yang terjadi? Inilah bagian yang sulit. Kelanjutan dari tugas penundaan tidak hanya memanggil Blah (). Itu juga harus memicu panggilan ke Bar () , tetapi tugas itu tidak tahu tentang Bar!
Foo benar-benar membuat delegasi yang (1) memanggil Blah (), dan (2) memanggil kelanjutan dari tugas yang Foo buat dan serahkan kembali ke pengendali acara. Begitulah cara kami memanggil delegasi yang memanggil Bar ().
Dan sekarang kami telah melakukan semua yang perlu kami lakukan, dalam urutan yang benar. Tapi kami tidak pernah berhenti memproses pesan dalam loop pesan terlalu lama, sehingga aplikasi tetap responsif.
Bahwa skenario ini terlalu maju untuk stack masuk akal, tetapi apa yang menggantikan stack?
Grafik objek tugas yang berisi referensi satu sama lain melalui kelas penutupan delegasi. Kelas-kelas penutupan adalah mesin negara yang melacak posisi menunggu yang paling baru dieksekusi dan nilai-nilai penduduk setempat. Plus, dalam contoh yang diberikan, antrian tindakan negara-global diimplementasikan oleh sistem operasi dan loop pesan yang mengeksekusi tindakan tersebut.
Latihan: bagaimana Anda mengira ini semua bekerja di dunia tanpa loop pesan? Misalnya, aplikasi konsol. menunggu di aplikasi konsol sangat berbeda; dapatkah Anda menyimpulkan cara kerjanya dari apa yang Anda ketahui sejauh ini?
Ketika saya telah belajar tentang ini tahun lalu, tumpukan itu ada di sana karena kilat cepat dan ringan, sepotong memori yang dialokasikan pada aplikasi jauh dari tumpukan karena mendukung manajemen yang sangat efisien untuk tugas yang sedang ditangani (pun intended?). Apa yang berubah?
Tumpukan adalah struktur data yang berguna ketika masa hidup aktivasi metode membentuk tumpukan, tetapi dalam contoh saya aktivasi penangan klik, Foo, Bar dan Blah tidak membentuk tumpukan. Dan oleh karena itu struktur data yang menyatakan bahwa alur kerja tidak bisa menjadi tumpukan; melainkan grafik tugas yang dialokasikan heap dan delegasi yang mewakili alur kerja. Menunggu adalah poin dalam alur kerja di mana kemajuan tidak dapat dibuat lebih lanjut dalam alur kerja sampai pekerjaan dimulai lebih awal telah selesai; sementara kita menunggu, kita dapat melakukan pekerjaan lain yang tidak bergantung pada tugas-tugas awal tertentu yang telah selesai.
Tumpukan hanyalah array dari frame, di mana frame berisi (1) pointer ke tengah fungsi (di mana panggilan terjadi) dan (2) nilai variabel lokal dan temps. Kelanjutan tugas adalah hal yang sama: delegasi adalah pointer ke fungsi dan memiliki keadaan yang merujuk titik tertentu di tengah fungsi (di mana menunggu terjadi), dan penutupan memiliki bidang untuk setiap variabel lokal atau sementara . Frame hanya tidak membentuk array yang bagus lagi, tetapi semua informasinya sama.