Recomandări Cheie
- Concurența și paralelismul sunt principii fundamentale în execuția sarcinilor de calcul, fiecare având caracteristici specifice și distincte.
- Concurența optimizează utilizarea resurselor și îmbunătățește răspunsul aplicațiilor, în timp ce paralelismul este esențial pentru performanță superioară și scalabilitate.
- Python oferă instrumente pentru a gestiona concurența, cum ar fi threading și programarea asincronă cu asyncio, precum și paralelismul prin modulul de multiprocesare.
Concurența și paralelismul sunt două abordări prin care se pot rula mai multe segmente de cod simultan. Python pune la dispoziție diverse opțiuni pentru a manageria sarcinile în mod concurent și paralel, ceea ce poate crea uneori confuzie.
Vom explora instrumentele și bibliotecile disponibile pentru a implementa corect concurența și paralelismul în Python, evidențiind diferențele dintre ele.
Înțelegerea Concurenței și Paralelismului
Concurența și paralelismul sunt două concepte fundamentale în execuția sarcinilor computaționale, având fiecare caracteristici proprii.


Importanța Concurenței și Paralelismului
Necesitatea concurenței și paralelismului în calcul este evidentă. Iată motivele pentru care aceste tehnici sunt atât de importante:
Concurența în Python
În Python, concurența poate fi obținută prin threading și programare asincronă, folosind biblioteca asyncio.
Threading în Python
Threading-ul este un mecanism de concurență ce permite crearea și managementul sarcinilor în interiorul unui singur proces. Thread-urile sunt potrivite pentru anumite tipuri de sarcini, mai ales cele care sunt legate de operațiile de I/O, beneficiind de o execuție concurentă.
Modulul de threading al Python oferă o interfață pentru crearea și gestionarea firelor de execuție. Deși GIL (Global Interpreter Lock) limitează firele în termeni de paralelism real, acestea pot obține concurența prin intercalarea eficientă a sarcinilor.
Exemplul de cod de mai jos ilustrează implementarea concurenței folosind thread-uri. Se utilizează biblioteca requests pentru a trimite o solicitare HTTP, o operațiune frecventă de I/O, precum și modulul time pentru a măsura timpul de execuție.
import requests
import time
import threading urls = [ 'https://www.google.com', 'https://www.wikipedia.org', 'https://www.makeuseof.com', ] def download_url(url): response = requests.get(url) print(f"Descărcat {url} - Cod de stare: {response.status_code}") start_time = time.time() for url in urls: download_url(url) end_time = time.time() print(f"Descărcare secvențială a durat {end_time - start_time:.2f} secunde\n") start_time = time.time() threads = [] for url in urls: thread = threading.Thread(target=download_url, args=(url,)) thread.start() threads.append(thread) for thread in threads: thread.join() end_time = time.time() print(f"Descărcare cu fire de execuție a durat {end_time - start_time:.2f} secunde")
Rulând acest program, se poate observa că cererile cu fire de execuție sunt mai rapide decât cele secvențiale. Chiar dacă diferența este de doar câteva fracțiuni de secundă, se poate observa o îmbunătățire a performanței când se folosesc fire de execuție pentru activități legate de I/O.
Programare asincronă cu Asyncio
Asyncio oferă o buclă de evenimente care gestionează sarcinile asincrone, numite corutine. Corutinele sunt funcții ce pot fi întrerupte și reluate, fiind ideale pentru sarcini legate de I/O. Această bibliotecă este utilă în scenariile unde sarcinile implică așteptarea unor resurse externe, cum ar fi solicitări de rețea.
Exemplul anterior de trimitere a cererilor poate fi adaptat pentru a funcționa cu asyncio:
import asyncio
import aiohttp
import time urls = [ 'https://www.google.com', 'https://www.wikipedia.org', 'https://www.makeuseof.com', ] async def download_url(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: content = await response.text() print(f"Descărcat {url} - Cod de stare: {response.status}") async def main(): tasks = [download_url(url) for url in urls] await asyncio.gather(*tasks) start_time = time.time() asyncio.run(main()) end_time = time.time() print(f"Descărcare cu asyncio a durat {end_time - start_time:.2f} secunde")
Cu acest cod, paginile web sunt descărcate simultan, utilizând asyncio și operațiunile de I/O asincrone. Această metodă poate fi mai eficientă decât threading-ul pentru sarcini legate de I/O.
Paralelismul în Python
Paralelismul poate fi implementat cu modulul de multiprocesare al Python, care permite folosirea procesoarelor multi-core la capacitate maximă.
Multiprocesarea în Python
Modulul de multiprocesare din Python oferă o metodă de a realiza paralelismul prin crearea de procese separate, fiecare cu propriul interpretor Python și spațiu de memorie. Astfel, se ocolește Blocarea Globală a Interpretului (GIL), fiind potrivit pentru sarcinile legate de CPU.
import requests
import multiprocessing
import time urls = [ 'https://www.google.com', 'https://www.wikipedia.org', 'https://www.makeuseof.com', ] def download_url(url): response = requests.get(url) print(f"Descărcat {url} - Cod de stare: {response.status_code}") def main(): num_processes = len(urls) pool = multiprocessing.Pool(processes=num_processes) start_time = time.time() pool.map(download_url, urls) end_time = time.time() pool.close() pool.join() print(f"Descărcare cu multiprocesare a durat {end_time-start_time:.2f} secunde") main()
În acest exemplu, multiprocesarea creează mai multe procese, permițând funcției download_url să ruleze în paralel.
Când să folosim concurența sau paralelismul
Alegerea între concurență și paralelism depinde de natura sarcinilor și de resursele hardware disponibile.
Se recomandă utilizarea concurenței în cazul sarcinilor legate de I/O, cum ar fi citirea și scrierea în fișiere sau efectuarea de solicitări de rețea, și când constrângerile de memorie sunt o problemă.
Se utilizează multiprocesarea pentru sarcinile care necesită multă putere de calcul (CPU), care pot beneficia de paralelism real, precum și atunci când este necesară o izolare robustă între sarcini, astfel încât eșecul unei sarcini să nu le afecteze pe celelalte.
Beneficiați de Concurență și Paralelism
Paralelismul și concurența sunt metode eficiente de a îmbunătăți viteza de răspuns și performanța codului Python. Este important să înțelegeți diferențele dintre aceste concepte și să alegeți strategia cea mai eficientă.
Python oferă toate instrumentele și modulele necesare pentru a face codul mai eficient prin concurență sau paralelism, indiferent dacă se lucrează cu procese legate de CPU sau de I/O.