8.1. Konkurensi kooperatif¶
Model penjadwalan asyncio bersifat kooperatif, bukan preemptif. Perbedaan ini adalah model mental terpenting yang menjadi dasar sisa bagian ini, sehingga penting untuk dipahami sebelum kode apa pun muncul.
8.1.1. Preemptif vs kooperatif¶
Penjadwal preemptif — jenis yang digunakan sistem operasi desktop untuk menjalankan banyak program sekaligus — dapat menghentikan potongan kode yang sedang berjalan kapan saja dan beralih ke yang lain. Kode yang berjalan tidak perlu melakukan apa pun khusus; penjadwal menginterupsinya. Hal itu membuat penjadwalan preemptif sangat fleksibel (tidak ada potongan kode yang dapat membuat yang lain kelaparan karena lambat), tetapi juga berarti setiap variabel bersama harus dijaga dengan hati-hati, karena peralihan dapat terjadi di mana saja — bahkan di tengah penulisan nilai, atau di tengah pembacaan sebuah daftar.
Penjadwal kooperatif hanya dapat beralih antara potongan kode pada titik-titik di mana potongan yang sedang berjalan secara eksplisit menyerahkan kontrol kembali. Pada asyncio, titik-titik tersebut adalah setiap await dan setiap panggilan ke coroutine yang menghasilkan secara internal (paling umum asyncio.sleep()). Di antara dua await, coroutine yang berjalan memiliki CPU untuk dirinya sendiri.
Dua konsekuensi yang muncul dari hal itu:
Coroutine yang tidak pernah await tidak pernah dijeda. Jika sebuah coroutine berada dalam loop ketat tanpa
awaitdi dalamnya, ia akan memonopoli penjadwal dan tidak ada yang lain yang dapat berkembang. Solusinya adalahawait asyncio.sleep_ms(0)(atau panggilan menunggu lainnya) pada titik yang tepat dalam loop.Status bersama aman di antara await. Dua coroutine tidak dapat saling menyela di tengah operasi yang tidak memiliki
awaitdi dalamnya. Jenis kerusakan yang muncul ketika preemsi terjadi di tengah pembaruan multi-langkah — satu potongan kode membaca nilai sementara yang lain sedang dalam proses mengubahnya — tidak bisa terjadi di sini. Koordinasi antara coroutine tetap diperlukan ketika beberapa dari mereka harus berbagi sumber daya di seluruh await, tetapi masalah interleaving di-tengah-baris tidak berlaku.
8.1.2. Tiga lapisan¶
Setiap skrip asyncio dibangun dari tiga lapisan yang sama. Dua halaman berikutnya membahasnya secara rinci; inilah label yang perlu diingat saat membacanya.
Coroutines — fungsi yang dideklarasikan dengan
async def, masing-masing merupakan unit pekerjaan mandiri yang melakukan await bila diperlukan. Ikhtisar Python memperkenalkan kata kunciasync/await; dalam asyncio, kata kunci tersebut adalah cara coroutine menyerahkan kembali ke penjadwal.Tasks — pembungkus yang
asyncio.create_task()pasang di sekeliling coroutine untuk menjadwalkannya secara bersamaan dengan yang saat ini. Aplikasi biasanya membuat sejumlah task untuk pekerjaan yang berjalan lama (loop snapshot, klien jaringan, pembaca UART, ...).Event loop — mesin di bawahnya yang melacak coroutine mana yang sedang menunggu dan mana yang siap dijalankan, beralih antara task di setiap
await. Aplikasi tidak menulis loop itu; ia menyerahkan coroutine tingkat atas keasyncio.run()dan loop yang mengendalikan segalanya dari sana.
Ketika aplikasi digambarkan dengan cara itu — sebagai sekumpulan kecil coroutine yang disusun oleh event loop — konkurensi menjadi properti dari bentuk program, bukan sesuatu yang harus dikelola aplikasi langkah demi langkah.