3 langkah untuk perbaikan asinkron Python

Python adalah salah satu dari banyak bahasa yang mendukung beberapa cara untuk menulis program asinkron - program yang beralih secara bebas di antara banyak tugas, semuanya berjalan sekaligus, sehingga tidak ada tugas yang menahan kemajuan tugas lainnya.

Namun, kemungkinan besar Anda telah menulis program Python sinkron - program yang hanya melakukan satu hal pada satu waktu, menunggu setiap tugas selesai sebelum memulai yang lain. Pindah ke async bisa sangat mengganggu, karena memerlukan pembelajaran tidak hanya sintaks baru, tetapi juga cara baru dalam berpikir tentang kode seseorang. 

Dalam artikel ini, kita akan mempelajari bagaimana program sinkron yang ada dapat diubah menjadi program asinkron. Ini melibatkan lebih dari sekadar fungsi dekorasi dengan sintaksis asinkron; itu juga membutuhkan pemikiran yang berbeda tentang bagaimana program kita berjalan, dan memutuskan apakah async bahkan merupakan metafora yang baik untuk apa yang dilakukannya. 

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

Kapan menggunakan async dengan Python

Program Python paling cocok untuk async jika memiliki karakteristik berikut:

  • Ini mencoba melakukan sesuatu yang sebagian besar terikat oleh I / O atau dengan menunggu beberapa proses eksternal selesai, seperti pembacaan jaringan yang berjalan lama.
  • Itu mencoba melakukan satu atau lebih dari jenis tugas itu sekaligus, sementara mungkin juga menangani interaksi pengguna.
  • Tugas yang dimaksud tidak berat secara komputasi.

Program Python yang menggunakan threading biasanya merupakan kandidat yang baik untuk menggunakan async. Benang dengan Python bersifat kooperatif; mereka mengalah satu sama lain sesuai kebutuhan. Tugas asinkron dengan Python bekerja dengan cara yang sama. Selain itu, asinkron menawarkan keuntungan tertentu dibandingkan utas:

  • The async/ awaitsintaks memudahkan untuk mengidentifikasi bagian-bagian asynchronous program Anda. Sebaliknya, sering kali sulit untuk mengetahui secara sekilas bagian mana dari aplikasi yang berjalan dalam sebuah thread. 
  • Karena tugas asinkron berbagi utas yang sama, data apa pun yang mereka akses dikelola secara otomatis oleh GIL (mekanisme asli Python untuk menyinkronkan akses ke objek). Untaian sering kali memerlukan mekanisme rumit untuk sinkronisasi. 
  • Tugas asinkron lebih mudah dikelola dan dibatalkan daripada utas.

Menggunakan async tidak disarankan jika program Python Anda memiliki karakteristik berikut:

  • Tugas-tugas memiliki biaya komputasi yang tinggi - misalnya, mereka melakukan penghitungan angka yang berat. Pekerjaan komputasi yang berat paling baik ditangani multiprocessing, yang memungkinkan Anda mencurahkan seluruh utas perangkat keras untuk setiap tugas.
  • Tugas tidak mendapat manfaat dari interleaving. Jika setiap tugas bergantung pada yang terakhir, tidak ada gunanya membuatnya berjalan secara asinkron. Meskipun demikian, jika program melibatkan  serangkaian tugas serial, Anda dapat menjalankan setiap rangkaian secara asinkron.

Langkah 1: Identifikasi bagian sinkron dan asinkron dari program Anda

Kode asinkron Python harus diluncurkan oleh, dan dikelola oleh, bagian sinkron dari aplikasi Python Anda. Untuk itu, tugas pertama Anda saat mengonversi program menjadi asinkron adalah menggambar garis antara bagian sinkronisasi dan asinkron dari kode Anda.

Dalam artikel kami sebelumnya tentang asinkron, kami menggunakan aplikasi pengikis web sebagai contoh sederhana. Bagian asinkron dari kode adalah rutinitas yang membuka koneksi jaringan dan membaca dari situs - semua yang ingin Anda interleave. Tetapi bagian dari program yang memulai semua itu tidaklah async; itu meluncurkan tugas asinkron dan kemudian menutupnya dengan anggun saat mereka selesai.

Penting juga untuk memisahkan operasi yang berpotensi  memblokir dari asinkron, dan menyimpannya di bagian sinkronisasi aplikasi Anda. Membaca masukan pengguna dari konsol, misalnya, memblokir semuanya termasuk async event loop. Oleh karena itu, Anda ingin menangani masukan pengguna baik sebelum Anda meluncurkan tugas asinkron atau setelah Anda menyelesaikannya. (Hal ini dimungkinkan untuk menangani input pengguna asynchronous melalui multiprocessing atau threading, tapi itu latihan canggih kita tidak akan masuk ke sini.)

Beberapa contoh operasi pemblokiran:

  • Input konsol (seperti yang baru saja kami jelaskan).
  • Tugas yang melibatkan pemakaian CPU yang berat.
  • Menggunakan time.sleepuntuk memaksa jeda. Perhatikan bahwa Anda dapat tidur di dalam fungsi async dengan menggunakan asyncio.sleepsebagai pengganti time.sleep.

Langkah 2: Ubah fungsi sinkronisasi yang sesuai menjadi fungsi asinkron

Setelah Anda mengetahui bagian mana dari program Anda yang akan berjalan secara asinkron, Anda dapat mempartisi mereka menjadi fungsi (jika Anda belum melakukannya) dan mengubahnya menjadi fungsi asinkron dengan asynckata kunci. Kemudian Anda perlu menambahkan kode ke bagian sinkron dari aplikasi Anda untuk menjalankan kode asinkron dan mengumpulkan hasil darinya jika diperlukan.

Catatan: Anda sebaiknya memeriksa rantai panggilan dari setiap fungsi yang telah Anda buat asinkron, dan memastikan mereka tidak menjalankan operasi yang berpotensi berjalan lama atau memblokir. Fungsi asinkron bisa langsung memanggil fungsi sinkronisasi, dan jika fungsi sinkronisasi itu memblokir, begitu pula fungsi asinkron yang memanggilnya.

Mari kita lihat contoh sederhana tentang bagaimana konversi sinkronisasi-ke-asinkron dapat bekerja. Berikut adalah program "sebelum" kami:

def a_function (): # beberapa tindakan yang kompatibel dengan asinkron yang memerlukan beberapa saat def another_function (): # beberapa fungsi sinkronisasi, tetapi bukan salah satu fungsi yang memblokir def do_stuff (): a_function () another_function () def main (): for _ in range (3): do_stuff () main () 

Jika kita ingin tiga contoh do_stuffuntuk dijalankan sebagai tugas asinkron, kita perlu mengubah do_stuff (dan kemungkinan semua yang disentuhnya) menjadi kode asinkron. Ini adalah langkah pertama pada konversi:

import asyncio async def a_function (): # beberapa tindakan yang kompatibel dengan asinkron yang memerlukan beberapa saat def another_function (): # beberapa fungsi sinkronisasi, tetapi bukan pemblokiran async def do_stuff (): await a_function () another_function () async def main ( ): tugas = [] untuk _ dalam rentang (3): tugas.append (asyncio.create_task (do_stuff ())) menunggu asyncio.gather (tugas) asyncio.run (main ()) 

Perhatikan perubahan yang kami lakukan  main. Sekarang main menggunakan asynciountuk meluncurkan setiap instance do_stuffsebagai tugas bersamaan, lalu menunggu result ( asyncio.gather). Kami juga mengubahnya a_functionmenjadi fungsi asinkron, karena kami ingin semua instance a_functionberjalan berdampingan, dan di samping fungsi lain yang memerlukan perilaku asinkron.

Jika kami ingin melangkah lebih jauh, kami juga dapat mengonversi another_functionke asinkron:

async def another_function (): # beberapa fungsi sinkronisasi, tetapi bukan yang memblokir async def do_stuff (): await a_function () menunggu another_function () 

Namun, membuat  another_function asynchronous akan berlebihan, karena (seperti yang telah kami catat) itu tidak melakukan apa pun yang akan menghalangi kemajuan program kami. Selain itu, jika ada bagian sinkron dari program kita yang dipanggil  another_function, kita juga harus mengubahnya menjadi asinkron, yang dapat membuat program kita lebih rumit daripada yang seharusnya.

Langkah 3: Uji program asinkron Python Anda secara menyeluruh

Setiap program yang dikonversi asinkron perlu diuji sebelum masuk ke produksi untuk memastikannya bekerja seperti yang diharapkan.

Jika program Anda berukuran sederhana - katakanlah, beberapa lusin baris atau lebih - dan tidak memerlukan rangkaian pengujian lengkap, maka seharusnya tidak sulit untuk memverifikasi bahwa program itu berfungsi sebagaimana mestinya. Meskipun demikian, jika Anda mengonversi program menjadi asinkron sebagai bagian dari proyek yang lebih besar, di mana rangkaian pengujian adalah perlengkapan standar, masuk akal untuk menulis pengujian unit untuk komponen asinkron dan sinkronisasi.

Kedua kerangka pengujian utama di Python sekarang menampilkan beberapa jenis dukungan asinkron. unittest Kerangka kerja Python sendiri  menyertakan objek kasus uji untuk fungsi asinkron, dan pytestpenawaran  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 membuat profil kode Python
  • Cara menggunakan cProfile untuk membuat profil kode Python
  • Cara mengonversi Python ke JavaScript (dan kembali lagi)