Pengantar pemrograman asinkron dengan Python

Halo. Mempersiapkan terjemahan artikel yang menarik pada malam dimulainya kursus dasar "Pengembang Python" .








pengantar



Pemrograman asinkron adalah bentuk pemrograman paralel di mana unit kerja dapat dilakukan secara terpisah dari utas utama aplikasi. Ketika pekerjaan berakhir, utas utama diberitahu bahwa alur kerja telah selesai atau kesalahan telah terjadi. Ada banyak manfaat untuk pendekatan ini, seperti peningkatan kinerja aplikasi dan peningkatan kecepatan respons.







Pemrograman asinkron telah menerima banyak perhatian dalam beberapa tahun terakhir, dan untuk alasan yang baik. Meskipun jenis pemrograman ini bisa lebih kompleks daripada eksekusi berurutan tradisional, itu jauh lebih efisien.



Misalnya, alih-alih menunggu permintaan HTTP selesai sebelum melanjutkan, Anda dapat mengirim permintaan dan melakukan pekerjaan lain yang menunggu dalam antrean menggunakan coroutine asynchronous di Python.



Asynchrony adalah salah satu alasan utama untuk popularitas Node.js untuk implementasi backend. Banyak kode yang kita tulis, terutama di aplikasi berat I / O seperti situs web, tergantung pada sumber daya eksternal. Itu bisa berisi apa saja dari panggilan basis data jarak jauh ke permintaan POST ke layanan REST. Setelah Anda mengirimkan permintaan ke salah satu sumber daya ini, kode Anda hanya akan menunggu jawaban. Dengan pemrograman asinkron, Anda membiarkan kode Anda menangani tugas-tugas lain sambil menunggu respons dari sumber daya.



Bagaimana cara Python berhasil melakukan beberapa hal sekaligus?







1. Banyak Proses.



Cara yang paling jelas adalah menggunakan banyak proses. Dari terminal, Anda dapat menjalankan skrip Anda dua, tiga, empat, sepuluh kali, dan semua skrip akan berjalan secara independen dan simultan. Sistem operasi akan menangani distribusi sumber daya prosesor di antara semua instance. Atau, Anda dapat menggunakan pustaka multiprosesing , yang dapat menelurkan banyak proses, seperti yang ditunjukkan pada contoh di bawah ini.



from multiprocessing import Process

def print_func(continent='Asia'):
    print('The name of continent is : ', continent)

if __name__ == "__main__":  # confirms that the code is under main function
    names = ['America', 'Europe', 'Africa']
    procs = []
    proc = Process(target=print_func)  # instantiating without any argument
    procs.append(proc)
    proc.start()

    # instantiating process with arguments
    for name in names:
        # print(name)
        proc = Process(target=print_func, args=(name,))
        procs.append(proc)
        proc.start()

    # complete the processes
    for proc in procs:
        proc.join()


Keluaran:



The name of continent is :  Asia
The name of continent is :  America
The name of continent is :  Europe
The name of continent is :  Africa


2. Banyak Utas



Cara lain untuk menjalankan beberapa pekerjaan secara paralel adalah dengan menggunakan utas. Thread adalah antrian run yang sangat mirip dengan proses, namun dalam satu proses Anda dapat memiliki beberapa utas dan semuanya akan memiliki akses bersama ke sumber daya. Namun, ini akan membuatnya sulit untuk menulis kode aliran. Demikian juga, sistem operasi akan melakukan semua kerja keras mengalokasikan memori prosesor, tetapi kunci juru bahasa global (GIL) hanya akan memungkinkan satu utas Python berjalan pada satu waktu, bahkan jika Anda memiliki kode multithreaded. Ini adalah bagaimana GIL pada CPython mencegah konkurensi multi-core. Artinya, Anda dapat berjalan secara paksa hanya pada satu inti, bahkan jika Anda memiliki dua, empat atau lebih.



import threading
 
def print_cube(num):
    """
    function to print cube of given num
    """
    print("Cube: {}".format(num * num * num))
 
def print_square(num):
    """
    function to print square of given num
    """
    print("Square: {}".format(num * num))
 
if __name__ == "__main__":
    # creating thread
    t1 = threading.Thread(target=print_square, args=(10,))
    t2 = threading.Thread(target=print_cube, args=(10,))
 
    # starting thread 1
    t1.start()
    # starting thread 2
    t2.start()
 
    # wait until thread 1 is completely executed
    t1.join()
    # wait until thread 2 is completely executed
    t2.join()
 
    # both threads completely executed
    print("Done!")


Keluaran:



Square: 100
Cube: 1000
Done!


3. Coroutine dan yield:



Coroutine adalah generalisasi dari rutinitas. Mereka digunakan untuk multitasking kooperatif, di mana suatu proses secara sukarela menyerahkan kontrol ( yield) pada beberapa frekuensi atau selama periode menunggu untuk memungkinkan beberapa aplikasi berjalan pada waktu yang sama. Coroutine mirip dengan generator , tetapi dengan metode tambahan dan perubahan kecil dalam cara kami menggunakan pernyataan hasil . Generator menghasilkan data untuk iterasi, sementara coroutine juga dapat mengkonsumsi data.



def print_name(prefix):
    print("Searching prefix:{}".format(prefix))
    try : 
        while True:
                # yeild used to create coroutine
                name = (yield)
                if prefix in name:
                    print(name)
    except GeneratorExit:
            print("Closing coroutine!!")
 
corou = print_name("Dear")
corou.__next__()
corou.send("James")
corou.send("Dear James")
corou.close()


Keluaran:



Searching prefix:Dear
Dear James
Closing coroutine!!


4. Pemrograman asinkron



. Metode keempat adalah pemrograman asinkron, di mana sistem operasi tidak terlibat. Di sisi sistem operasi, Anda akan memiliki satu proses di mana hanya akan ada satu utas, tetapi Anda masih dapat melakukan beberapa tugas secara bersamaan. Jadi apa masalahnya?



Jawab: asyncio



Asyncio- Modul pemrograman asinkron yang diperkenalkan dengan Python 3.4. Ini dirancang untuk menggunakan coroutine dan futures untuk membuatnya lebih mudah untuk menulis kode asinkron dan membuatnya hampir dapat dibaca sebagai kode sinkron karena kurangnya callback.



Asynciomenggunakan desain yang berbeda :, event loopcoroutine dan future.



  • event loop . .
  • ( ) – , Python, await event loop. event loop. Tasks, Future.
  • Future , . exception.


Dengan bantuan asyncioAnda dapat menyusun kode Anda sehingga subtugas didefinisikan sebagai coroutine dan memungkinkan Anda untuk merencanakan peluncurannya seperti yang Anda inginkan, termasuk pada saat yang bersamaan. Coroutine berisi poin yielddi mana kami mengidentifikasi kemungkinan titik peralihan konteks. Jika ada tugas dalam antrian tunggu, konteksnya akan dialihkan, jika tidak, tidak.



Switch konteks asyncioadalah event loop, yang mentransfer aliran kontrol dari satu coroutine ke yang lain.



Dalam contoh berikut, kami menjalankan 3 tugas tidak sinkron yang secara individual membuat permintaan untuk Reddit, mengambil dan mengeluarkan konten JSON. Kami menggunakan aiohttp - Pustaka klien http, yang memastikan bahwa bahkan permintaan HTTP akan dijalankan secara tidak sinkron.



import signal  
import sys  
import asyncio  
import aiohttp  
import json

loop = asyncio.get_event_loop()  
client = aiohttp.ClientSession(loop=loop)

async def get_json(client, url):  
    async with client.get(url) as response:
        assert response.status == 200
        return await response.read()

async def get_reddit_top(subreddit, client):  
    data1 = await get_json(client, 'https://www.reddit.com/r/' + subreddit + '/top.json?sort=top&t=day&limit=5')

    j = json.loads(data1.decode('utf-8'))
    for i in j['data']['children']:
        score = i['data']['score']
        title = i['data']['title']
        link = i['data']['url']
        print(str(score) + ': ' + title + ' (' + link + ')')

    print('DONE:', subreddit + '\n')

def signal_handler(signal, frame):  
    loop.stop()
    client.close()
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)

asyncio.ensure_future(get_reddit_top('python', client))  
asyncio.ensure_future(get_reddit_top('programming', client))  
asyncio.ensure_future(get_reddit_top('compsci', client))  
loop.run_forever()


Keluaran:



50: Undershoot: Parsing theory in 1965 (http://jeffreykegler.github.io/Ocean-of-Awareness-blog/individual/2018/07/knuth_1965_2.html)
12: Question about best-prefix/failure function/primal match table in kmp algorithm (https://www.reddit.com/r/compsci/comments/8xd3m2/question_about_bestprefixfailure_functionprimal/)
1: Question regarding calculating the probability of failure of a RAID system (https://www.reddit.com/r/compsci/comments/8xbkk2/question_regarding_calculating_the_probability_of/)
DONE: compsci

336: /r/thanosdidnothingwrong -- banning people with python (https://clips.twitch.tv/AstutePluckyCocoaLitty)
175: PythonRobotics: Python sample codes for robotics algorithms (https://atsushisakai.github.io/PythonRobotics/)
23: Python and Flask Tutorial in VS Code (https://code.visualstudio.com/docs/python/tutorial-flask)
17: Started a new blog on Celery - what would you like to read about? (https://www.python-celery.com)
14: A Simple Anomaly Detection Algorithm in Python (https://medium.com/@mathmare_/pyng-a-simple-anomaly-detection-algorithm-2f355d7dc054)
DONE: python

1360: git bundle (https://dev.to/gabeguz/git-bundle-2l5o)
1191: Which hashing algorithm is best for uniqueness and speed? Ian Boyd's answer (top voted) is one of the best comments I've seen on Stackexchange. (https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed)
430: ARM launches “Facts” campaign against RISC-V (https://riscv-basics.com/)
244: Choice of search engine on Android nuked by “Anonymous Coward” (2009) (https://android.googlesource.com/platform/packages/apps/GlobalSearch/+/592150ac00086400415afe936d96f04d3be3ba0c)
209: Exploiting freely accessible WhatsApp data or “Why does WhatsApp web know my phone’s battery level?” (https://medium.com/@juan_cortes/exploiting-freely-accessible-whatsapp-data-or-why-does-whatsapp-know-my-battery-level-ddac224041b4)
DONE: programming


Menggunakan Redis dan Redis Queue RQ



Penggunaan asynciodan aiohttptidak selalu merupakan ide yang baik, terutama jika Anda menggunakan versi Python yang lebih lama. Selain itu, ada saatnya Anda perlu mendistribusikan tugas di berbagai server. Dalam hal ini, RQ (Redis Queue) dapat digunakan. Ini adalah pustaka Python umum untuk menambahkan pekerjaan ke antrian dan memprosesnya oleh pekerja di latar belakang. Untuk mengatur antrian, Redis digunakan - basis data kunci / nilai.



Pada contoh di bawah ini, kami menambahkan fungsi sederhana ke antrian count_words_at_urlmenggunakan Redis.



from mymodule import count_words_at_url
from redis import Redis
from rq import Queue


q = Queue(connection=Redis())
job = q.enqueue(count_words_at_url, 'http://nvie.com')


******mymodule.py******

import requests

def count_words_at_url(url):
    """Just an example function that's called async."""
    resp = requests.get(url)

    print( len(resp.text.split()))
    return( len(resp.text.split()))


Keluaran:



15:10:45 RQ worker 'rq:worker:EMPID18030.9865' started, version 0.11.0
15:10:45 *** Listening on default...
15:10:45 Cleaning registries for queue: default
15:10:50 default: mymodule.count_words_at_url('http://nvie.com') (a2b7451e-731f-4f31-9232-2b7e3549051f)
322
15:10:51 default: Job OK (a2b7451e-731f-4f31-9232-2b7e3549051f)
15:10:51 Result is kept for 500 seconds


Kesimpulan



Sebagai contoh, mari kita ambil pameran catur di mana salah satu pemain catur terbaik bersaing dengan sejumlah besar orang. Kami memiliki 24 permainan dan 24 orang untuk dimainkan, dan jika seorang pemain catur bermain secara serempak dengan mereka, itu akan memakan waktu setidaknya 12 jam (dengan asumsi rata-rata permainan membutuhkan 30 gerakan, pemain catur berpikir untuk bergerak dalam 5 detik, dan lawan membutuhkan waktu sekitar 55 detik.) Namun, dalam mode asinkron, pemain catur akan dapat bergerak dan memberikan waktu bagi lawan untuk berpikir, sambil bergerak ke lawan berikutnya dan membagi gerakan. Dengan demikian, Anda dapat bergerak dalam semua 24 pertandingan dalam 2 menit, dan semuanya bisa dimenangkan hanya dalam satu jam.



Inilah yang tersirat ketika orang mengatakan bahwa sinkronisasi tidak membuat segalanya lebih cepat. Kita berbicara tentang kecepatan seperti itu. Seorang pemain catur yang baik tidak mulai bermain catur lebih cepat, hanya saja waktunya lebih dioptimalkan dan tidak sia-sia menunggu. Begini Cara kerjanya.



Dengan analogi ini, pemain catur akan menjadi prosesor, dan ide utamanya adalah untuk menjaga prosesor sesedikit mungkin. Ini tentang selalu melakukan sesuatu.



Dalam praktiknya, asinkron didefinisikan sebagai gaya pemrograman paralel di mana beberapa tugas membebaskan prosesor selama periode menunggu sehingga tugas lain dapat menggunakannya. Python memiliki beberapa cara untuk mencapai konkurensi yang sesuai dengan kebutuhan Anda, aliran kode, penanganan data, arsitektur, dan kasus penggunaan, dan Anda dapat memilih salah satu dari mereka.






.







All Articles