3 langkah untuk baik pulih async Python

Python adalah salah satu daripada banyak bahasa yang menyokong beberapa cara untuk menulis program tak segerak - program yang bertukar bebas di antara pelbagai tugas, semuanya berjalan serentak, sehingga tidak ada satu tugas yang menahan kemajuan yang lain.

Walaupun begitu, kemungkinan besar anda telah menulis program Python segerak - program yang hanya melakukan satu perkara pada satu masa, menunggu setiap tugas selesai sebelum memulakan yang lain. Beralih ke async boleh menjadi sangat membingungkan, kerana memerlukan pembelajaran bukan hanya sintaks baru, tetapi juga cara baru memikirkan kod seseorang. 

Dalam artikel ini, kita akan menerangkan bagaimana program segerak yang ada dapat diubah menjadi program tidak segerak. Ini melibatkan lebih daripada sekadar fungsi menghias dengan sintaks asinkron; ia juga memerlukan pemikiran yang berbeza mengenai bagaimana program kita berjalan, dan memutuskan sama ada async bahkan merupakan metafora yang baik untuk apa yang dilakukannya. 

[Juga di: Pelajari petua dan trik Python dari video Smart Python Serdar Yegulalp]

Bilakah menggunakan async di Python

Program Python sangat sesuai untuk async apabila mempunyai ciri-ciri berikut:

  • Ia cuba melakukan sesuatu yang kebanyakannya terikat oleh I / O atau dengan menunggu beberapa proses luaran selesai, seperti rangkaian yang telah lama dibaca.
  • Ia berusaha melakukan satu atau lebih jenis tugas itu sekaligus, sementara mungkin juga menangani interaksi pengguna.
  • Tugas yang dimaksudkan tidak berat secara komputasi.

Program Python yang menggunakan threading biasanya merupakan calon yang baik untuk menggunakan async. Benang di Python adalah koperasi; mereka saling menghormati mengikut keperluan. Tugas async di Python berfungsi dengan cara yang sama. Plus, async menawarkan kelebihan tertentu berbanding utas:

  • The async/ awaitsintaks menjadikannya mudah untuk mengenal pasti bahagian-bahagian yang tak segerak program anda. Sebaliknya, seringkali sukar untuk mengetahui sekilas bahagian aplikasi yang dijalankan dalam utas. 
  • Oleh kerana tugas async mempunyai utas yang sama, setiap data yang mereka akses dikendalikan secara automatik oleh GIL (mekanisme asli Python untuk menyegerakkan akses ke objek). Benang sering memerlukan mekanisme yang kompleks untuk penyegerakan. 
  • Tugas async lebih mudah diuruskan dan dibatalkan daripada utas.

Menggunakan async tidak digalakkan jika program Python anda mempunyai ciri-ciri berikut:

  • Tugas-tugas tersebut mempunyai kos pengiraan yang tinggi - misalnya, mereka melakukan pengurangan jumlah yang berat. Kerja komputasi berat ditangani dengan sebaik-baiknya multiprocessing, yang membolehkan anda menumpukan keseluruhan utas perkakasan untuk setiap tugas.
  • Tugas-tugas tidak mendapat faedah daripada disisipkan. Sekiranya setiap tugas bergantung pada tugas terakhir, tidak ada gunanya membuatnya berjalan secara tidak segerak. Walaupun begitu, jika program ini melibatkan  set tugas bersiri, anda dapat menjalankan setiap set secara tidak serentak.

Langkah 1: Kenal pasti bahagian segerak dan tidak segerak program anda

Kod async Python mesti dilancarkan oleh, dan dikendalikan oleh, bahagian segerak aplikasi Python anda. Untuk tujuan itu, tugas pertama anda ketika menukar program ke async adalah menarik garis antara bahagian penyegerakan dan async kod anda.

Dalam artikel sebelumnya mengenai async, kami menggunakan aplikasi pengikis web sebagai contoh mudah. Bahagian kod async adalah rutin yang membuka sambungan rangkaian dan membaca dari laman web - semua yang anda mahu campurkan. Tetapi bahagian program yang memulakan semua itu tidak segerak; ia melancarkan tugas tak segerak dan kemudian menutupnya dengan anggun ketika mereka selesai.

Ia juga penting untuk memisahkan operasi yang berpotensi  menyekat dari async, dan menyimpannya di bahagian segerak aplikasi anda. Membaca input pengguna dari konsol, misalnya, menyekat semua perkara termasuk gelung peristiwa tak segerak. Oleh itu, anda ingin mengendalikan input pengguna sama ada sebelum anda melancarkan tugas penyegerakan atau setelah anda menyelesaikannya. (Ia adalah mungkin untuk input pengguna mengendalikan asynchronously melalui multiprocessing atau threading, tetapi itu satu latihan termaju kami tidak akan masuk ke sini.)

Beberapa contoh operasi penyekat:

  • Input konsol (seperti yang telah kami jelaskan).
  • Tugas yang melibatkan penggunaan CPU yang berat.
  • Menggunakan time.sleepuntuk memaksa jeda. Perhatikan bahawa anda boleh tidur di dalam fungsi async dengan menggunakan asyncio.sleepsebagai pengganti time.sleep.

Langkah 2: Tukar fungsi penyegerakan yang sesuai ke fungsi asinkron

Setelah anda mengetahui bahagian mana dari program anda yang akan dijalankan secara tidak segerak, anda boleh membahagikannya menjadi fungsi (jika belum) dan mengubahnya menjadi fungsi tak segerak dengan asynckata kunci. Anda kemudian perlu menambahkan kod ke bahagian segerak aplikasi anda untuk menjalankan kod async dan mengumpulkan hasil darinya jika diperlukan.

Catatan: Anda ingin memeriksa rantai panggilan setiap fungsi yang anda buat secara tidak segerak, dan pastikan mereka tidak menjalankan operasi yang mungkin berpanjangan atau menyekat. Fungsi async secara langsung dapat memanggil fungsi penyegerakan, dan jika fungsi sinkronisasi menyekat, maka fungsi async memanggilnya.

Mari kita lihat contoh ringkas bagaimana penukaran penyegerakan ke asinkron mungkin berfungsi. Inilah program "sebelum" kami:

def a_function (): # beberapa tindakan serasi dengan async yang memerlukan beberapa saat def another_function (): # beberapa fungsi penyegerakan, tetapi tidak menyekat satu def do_stuff (): a_function () another_function () def main (): for _ in range (3): do_stuff () utama () 

Sekiranya kita mahukan tiga kejadian do_stuffdijalankan sebagai tugas asinkron, kita perlu mengubah do_stuff (dan berpotensi semua yang disentuhnya) menjadi kod tak segerak. Berikut adalah hantaran pertama pada penukaran:

import asyncio async def a_function (): # beberapa tindakan serasi async yang memerlukan sedikit masa def lain_fungsi (): # beberapa fungsi penyegerakan, tetapi tidak menyekat satu async def do_stuff (): tunggu a_function () another_function () async def main () ): task = [] untuk _ dalam jarak (3): task.append (asyncio.create_task (do_stuff ())) menunggu asyncio.gather (task) asyncio.run (utama ()) 

Perhatikan perubahan yang kami buat  main. Sekarang main menggunakan asynciountuk melancarkan setiap contoh do_stuffsebagai tugas bersamaan, kemudian menunggu hasilnya ( asyncio.gather). Kami juga bertukar a_functionmenjadi fungsi async, kerana kami ingin semua kejadian a_functionberjalan berdampingan, dan bersama fungsi lain yang memerlukan perilaku async.

Sekiranya kita ingin melangkah lebih jauh, kita juga boleh menukar another_functionke async:

async def another_function (): # beberapa fungsi penyegerakan, tetapi tidak menghalang satu async def do_stuff (): tunggu a_fungsi () tunggu fungsi_ lain () 

Walau bagaimanapun, membuat  another_function asinkron akan berlebihan, kerana (seperti yang telah kita maklum) ia tidak melakukan apa-apa yang akan menghalang kemajuan program kita. Juga, jika ada bahagian sinkron dari program kami yang disebut  another_function, kami harus mengubahnya menjadi tidak segerak, yang dapat menjadikan program kami lebih rumit daripada yang seharusnya.

Langkah 3: Uji program async Python anda dengan teliti

Mana-mana program yang ditukar async perlu diuji sebelum masuk ke dalam pengeluaran untuk memastikan ia berfungsi seperti yang diharapkan.

Sekiranya program anda bersaiz sederhana - katakanlah, beberapa lusin baris atau lebih - dan tidak memerlukan rangkaian ujian penuh, maka tidak sukar untuk mengesahkan bahawa ia berfungsi seperti yang diharapkan. Walaupun begitu, jika anda menukar program ke async sebagai sebahagian daripada projek yang lebih besar, di mana suite ujian adalah lekapan standard, masuk akal untuk menulis ujian unit untuk komponen async dan sync.

Kedua-dua kerangka ujian utama di Python kini mempunyai beberapa jenis sokongan async. unittest Rangka kerja Python sendiri  merangkumi objek kes ujian untuk fungsi async, dan pytestmenawarkan  pytest-asynciountuk tujuan yang sama.

Finally, when writing tests for async components, you’ll need to handle their very asynchronousness as a condition of the tests. For instance, there is no guarantee that async jobs will complete in the order they were submitted. The first one might come in last, and some might never complete at all. Any tests you design for an async function must take these possibilities into account.

How to do more with Python

  • Get started with async in Python
  • How to use asyncio in Python
  • How to use PyInstaller to create Python executables
  • Cython tutorial: How to speed up Python
  • How to install Python the smart way
  • How to manage Python projects with Poetry
  • How to manage Python projects with Pipenv
  • Virtualenv and venv: Python virtual environments explained
  • Python virtualenv and venv do’s and don’ts
  • Python threading and subprocesses explained
  • How to use the Python debugger
  • Cara menggunakan timeit untuk profil kod Python
  • Cara menggunakan cProfile untuk profil kod Python
  • Cara menukar Python ke JavaScript (dan kembali lagi)