Jak vylepšit svůj kód Python pomocí souběžnosti a paralelnosti
Zásadní body
- Souběžnost a paralelismus představují základní metody pro provádění úkolů v oblasti výpočetní techniky, přičemž každá z nich se vyznačuje specifickými charakteristikami.
- Souběžnost umožňuje efektivní využití systémových zdrojů a zvyšuje reaktivitu aplikací, zatímco paralelismus je klíčový pro dosažení špičkového výkonu a škálovatelnosti.
- V jazyce Python existují nástroje pro práci se souběžností, jako je vytváření vláken a asynchronní programování s knihovnou asyncio, a také paralelismus prostřednictvím modulu multiprocessing.
Souběžnost a paralelismus představují dvě odlišné metody, které umožňují provádět více programů současně. Python nabízí několik přístupů k zpracování úloh souběžně i paralelně, což může být pro někoho matoucí.
Prozkoumejte dostupné nástroje a knihovny pro správnou implementaci souběžnosti a paralelismu v Pythonu a pochopte rozdíly mezi nimi.
Porozumění souběžnosti a paralelismu
Souběžnost a paralelismus jsou dva klíčové koncepty v oblasti výpočetní techniky. Každý z nich má své unikátní vlastnosti.
- Souběžnost se týká schopnosti programu spravovat více úkolů zdánlivě současně, aniž by byly nutně spouštěny přesně ve stejný okamžik. Klíčová je zde myšlenka prokládání úkolů a přepínání mezi nimi, což vytváří dojem simultánního provádění.
- Naopak, paralelismus zahrnuje skutečné provádění více úkolů současně. Obvykle se využívá více jader CPU nebo procesorů. Paralelismus dosahuje reálného souběžného provádění, což umožňuje provádět úkoly rychleji a je ideální pro výpočetně náročné operace.

Význam souběžnosti a paralelismu
Potřeba souběžnosti a paralelismu ve výpočetní technice je obrovská. Zde jsou hlavní důvody, proč jsou tyto metody tak důležité:
- Využití zdrojů: Souběžnost umožňuje efektivní využití systémových zdrojů a zajišťuje, že úkoly aktivně postupují, namísto nečinného čekání na externí zdroje.
- Reakce: Souběžnost dokáže výrazně zlepšit odezvu aplikací, zejména v situacích s uživatelskými rozhraními nebo webovými servery.
- Výkon: Paralelismus je zásadní pro dosažení optimálního výkonu, zvláště u úloh náročných na CPU, jako jsou komplexní výpočty, zpracování velkých dat a simulace.
- Škálovatelnost: Jak souběžnost, tak paralelismus jsou nezbytné pro vytváření škálovatelných systémů.
- Připravenost na budoucnost: S ohledem na vývoj hardwaru, který stále více upřednostňuje vícejádrové procesory, se paralelismus stává stále důležitější.
Souběžnost v Pythonu
V Pythonu lze dosáhnout souběžnosti pomocí vláken a asynchronního programování s knihovnou asyncio.
Vlákna v Pythonu
Vlákna jsou mechanismem souběžnosti v Pythonu, který umožňuje vytvářet a spravovat úlohy v rámci jednoho procesu. Jsou vhodné zejména pro úlohy vázané na I/O, které mohou těžit z souběžného provádění.
Modul threading v Pythonu poskytuje rozhraní na vysoké úrovni pro vytváření a správu vláken. Přestože GIL (Global Interpreter Lock) omezuje vlákna z hlediska skutečného paralelismu, mohou stále dosáhnout souběžnosti efektivním prokládáním úloh.
Následující kód demonstruje implementaci souběžnosti pomocí vláken. Používá knihovnu requests k odeslání HTTP požadavků, což je běžný I/O blokující úkon, a také modul time k měření doby provádění.
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"Downloaded {url} - Status Code: {response.status_code}")
start_time = time.time()
for url in urls:
download_url(url)
end_time = time.time()
print(f"Sequential download took {end_time - start_time:.2f} seconds\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"Threaded download took {end_time - start_time:.2f} seconds")
Při spuštění tohoto programu by mělo být patrné, že požadavky prováděné vlákny jsou výrazně rychlejší než sekvenční požadavky. I když je rozdíl jen v zlomcích sekund, je jasné zlepšení výkonu při použití vláken pro úlohy vázané na I/O.

Asynchronní programování s Asyncio
Knihovna asyncio nabízí smyčku událostí, která spravuje asynchronní úlohy, známé jako korutiny. Korutiny jsou funkce, které lze pozastavit a obnovit, což je činí ideální pro úlohy vázané na I/O. Tato knihovna je obzvláště užitečná v situacích, kdy úlohy zahrnují čekání na externí zdroje, například síťové požadavky.
Předchozí příklad odesílání požadavků lze modifikovat, aby pracoval s 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"Downloaded {url} - Status Code: {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"Asyncio download took {end_time - start_time:.2f} seconds")
Pomocí tohoto kódu je možné stahovat webové stránky souběžně s využitím asyncia a využít výhod asynchronních I/O operací. To se může ukázat efektivnější pro úlohy vázané na I/O než použití vláken.

Paralelismus v Pythonu
Paralelismus lze implementovat pomocí modulu multiprocessing v Pythonu, který umožňuje plně využít potenciálu vícejádrových procesorů.
Multiprocessing v Pythonu
Modul multiprocessing v Pythonu poskytuje způsob, jak dosáhnout paralelismu vytvořením samostatných procesů, z nichž každý má svůj vlastní interpret Pythonu a paměťový prostor. Tímto způsobem se efektivně obchází Global Interpreter Lock (GIL), což je výhodné pro úlohy vázané na 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"Downloaded {url} - Status Code: {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"Multiprocessing download took {end_time-start_time:.2f} seconds")
main()
V tomto příkladu multiprocessing vytváří několik procesů, což umožňuje funkci download_url běžet paralelně.

Kdy použít souběžnost nebo paralelismus
Volba mezi souběžností a paralelismem závisí na charakteru úloh a dostupných hardwarových prostředcích.
Souběžnost je vhodná pro úlohy vázané na I/O, jako je čtení a zápis do souborů nebo provádění síťových požadavků, a také tehdy, když jsou omezené paměťové zdroje.
Multiprocessing je naopak vhodný pro úlohy vázané na CPU, které mohou těžit z reálného paralelismu, a také v situacích, kdy je potřeba silná izolace mezi úlohami, aby selhání jedné z nich nemělo dopad na ostatní.
Využití výhod souběžnosti a paralelismu
Paralelismus a souběžnost jsou efektivní způsoby, jak zlepšit odezvu a výkon kódu v Pythonu. Je klíčové pochopit rozdíly mezi těmito koncepty a vybrat nejvhodnější strategii.
Python nabízí potřebné nástroje a moduly pro optimalizaci kódu pomocí souběžnosti nebo paralelismu, ať už pracujete s úlohami vázanými na CPU, nebo na I/O.