Cara menggunakan asyncio di Python

Fungsi pengaturcaraan asynchronous Python, atau ringkasnya async, membolehkan anda menulis program yang menyelesaikan lebih banyak kerja dengan tidak menunggu tugas bebas selesai. The asyncioperpustakaan termasuk dengan Python memberi anda alat untuk penggunaan async untuk memproses cakera atau rangkaian I / O tanpa membuat segala-galanya tunggu lagi.

asyncio menyediakan dua jenis API untuk menangani operasi tak segerak:  tahap tinggi  dan  tahap rendah . API peringkat tinggi adalah yang paling berguna, dan boleh digunakan untuk pelbagai aplikasi. API tahap rendah kuat, tetapi juga kompleks, dan jarang digunakan.

Kami akan menumpukan perhatian pada API peringkat tinggi dalam artikel ini. Pada bahagian di bawah, kita akan melihat API tahap tinggi yang paling biasa digunakan  asyncio, dan menunjukkan bagaimana ia boleh digunakan untuk operasi biasa yang melibatkan tugas tak segerak. 

Sekiranya anda benar-benar baru menggunakan async di Python, atau anda boleh menggunakan penyegaran semula mengenai cara kerjanya, baca pengenalan saya mengenai Python async sebelum menyelam di sini.

Jalankan coroutine dan tugas di Python

Secara semula jadi, penggunaan yang paling biasa asyncioadalah menjalankan bahagian asinkron skrip Python anda. Ini bermaksud belajar bekerja dengan coroutine dan tugas. 

Komponen async Python, termasuk coroutine dan tugas, hanya dapat digunakan dengan komponen async lain, dan bukan dengan Python segerak konvensional, jadi anda perlu  asyncio merapatkan jurang. Untuk melakukan ini, anda menggunakan  asyncio.run fungsi:

import asyncio

async def main ():

cetak ("Menunggu 5 saat.")

untuk _ dalam jarak (5):

tunggu asyncio.sleep (1)

cetak (".")

cetak ("Selesai menunggu.")

asyncio.run (utama ())

Ini berjalan  main(), bersamaan dengan main() kebakaran coroutine  , dan menunggu hasilnya kembali.

Sebagai peraturan umum, program Python hanya boleh mempunyai satu  .run() pernyataan, sama seperti program Python hanya boleh mempunyai satu  main() fungsi. Async, jika digunakan secara cuai, dapat menjadikan aliran kawalan program sukar dibaca. Mempunyai satu titik masuk ke kod asinkron program menjadikan perkara tidak menjadi berbulu.

Fungsi async juga dapat dijadwalkan sebagai  tugas , atau objek yang membungkus coroutin dan membantu menjalankannya.

async def my_task ():

lakukan sesuatu()

task = asyncio.create_task (my_task ())

my_task() kemudian dijalankan dalam gelung acara, dengan hasilnya disimpan di  task.

Sekiranya anda hanya mempunyai satu tugas yang ingin anda dapatkan hasilnya, anda boleh gunakan  asyncio.wait_for(task) untuk menunggu tugas selesai, kemudian gunakan  task.result() untuk mendapatkan hasilnya. Tetapi jika anda telah menjadualkan sejumlah tugas untuk dilaksanakan dan anda ingin menunggu  semuanya  selesai, gunakan  asyncio.wait([task1, task2]) untuk mengumpulkan hasilnya. (Perhatikan bahawa anda boleh menetapkan batas waktu operasi jika anda tidak mahu operasi berjalan melewati jangka waktu tertentu.)

Urus gelung peristiwa tak segerak di Python

Satu lagi kegunaan biasa bagi  asyncio adalah untuk menguruskan async  acara gelung . Gelung peristiwa adalah objek yang menjalankan fungsi dan panggil balik async; ia dibuat secara automatik semasa anda menggunakan  asyncio.run(). Anda biasanya ingin menggunakan hanya satu gelung peristiwa tak segerak setiap program, sekali lagi untuk memastikan semuanya dapat dikendalikan.

Sekiranya anda menulis perisian yang lebih maju, seperti pelayan, anda memerlukan akses tahap rendah ke gelung acara. Untuk itu, anda boleh "mengangkat tudung" dan bekerja secara langsung dengan bahagian dalaman gelung acara. Tetapi untuk pekerjaan sederhana anda tidak perlu.

Baca dan tulis data dengan aliran di Python

Senario terbaik untuk async adalah operasi rangkaian yang berjalan lama, di mana aplikasi mungkin menyekat menunggu beberapa sumber lain untuk menghasilkan hasil. Untuk itu,  asyncio menawarkan aliran, yang merupakan mekanisme tahap tinggi untuk melakukan I / O rangkaian. Ini termasuk bertindak sebagai pelayan untuk permintaan rangkaian.

asyncio menggunakan dua kelas,  StreamReader dan  StreamWriter, untuk membaca dan menulis dari rangkaian pada tahap tinggi. Sekiranya anda ingin membaca dari rangkaian, anda akan menggunakan  asyncio.open_connection() untuk membuka sambungan. Fungsi itu mengembalikan tupel  StreamReader dan  StreamWriter objek, dan anda akan menggunakan  .read() dan  .write() kaedah pada masing-masing untuk berkomunikasi.

Untuk menerima sambungan dari hos jauh, gunakan  asyncio.start_server(). Yang asyncio.start_server()fungsi mengambil sebagai hujah fungsi panggil balik,  client_connected_cb, yang dipanggil bila-bila masa ia menerima permintaan. Fungsi panggil balik itu mengambil contoh  StreamReader dan StreamWriter sebagai argumen, sehingga anda dapat menangani logik membaca / menulis untuk pelayan. (Lihat di sini untuk contoh pelayan HTTP sederhana yang menggunakan   perpustakaan asyncio-driven  aiohttp.)

Segerakkan tugas di Python

Tugas asinkron cenderung berjalan secara terpisah, tetapi kadang-kadang anda mahu mereka berkomunikasi antara satu sama lain. asyncio menyediakan barisan dan beberapa mekanisme lain untuk menyegerakkan antara tugas:

  • Antreanasyncio antrian membolehkan fungsi tak segerak untuk membariskan objek Python untuk dimakan oleh fungsi asinkron lain - misalnya, untuk mengedarkan beban kerja antara pelbagai jenis fungsi berdasarkan tingkah laku mereka.
  • Primitif penyegerakan : Kunci, peristiwa, keadaan, dan semafor dalam asynciokerja seperti rakan-rakan Python konvensional mereka. 

Satu perkara yang perlu diingat mengenai semua kaedah ini ialah kaedah ini  tidak  selamat. Ini bukan masalah untuk tugas asinkron yang berjalan dalam gelung peristiwa yang sama. Tetapi jika anda cuba berkongsi maklumat dengan tugas dalam gelung peristiwa, urutan OS, atau proses yang berbeza, anda perlu menggunakan  threading modul dan objeknya untuk melakukannya.

Selanjutnya, jika anda ingin  melancarkan  coroutine melintasi batas utas, gunakan  asyncio.run_coroutine_threadsafe() fungsinya, dan lulus gelung peristiwa untuk digunakan dengannya sebagai parameter.

Jeda coroutine di Python

Penggunaan lain yang biasa  asyncio, dan yang sedang dibincangkan, sedang menunggu masa yang sewenang-wenang di dalam coroutine. Anda tidak boleh menggunakannya  time.sleep() untuk ini, atau anda akan menyekat keseluruhan program. Sebaliknya, gunakan  asyncio.sleep(), yang membolehkan coroutine lain terus berjalan.

Gunakan async tahap bawah di Python

Akhirnya, jika anda berpendapat bahawa aplikasi yang anda bina mungkin memerlukan asynciokomponen tingkat bawah, lihatlah sebelum anda mula membuat pengekodan: Ada kemungkinan seseorang telah membina perpustakaan Python berkuasa async yang melakukan apa yang anda perlukan.

Sebagai contoh, jika anda memerlukan pertanyaan DNS async, periksa  aiodns perpustakaan, dan untuk sesi SSH async, ada  asyncSSH. Cari PyPI dengan kata kunci "async" (ditambah kata kunci yang berkaitan dengan tugas lain), atau periksa senarai Asyncio Awesome yang dikendalikan sendiri untuk mendapatkan idea.