Pro paralelní programování existuje v Pythonu několik modulů:
subprocess
threading
multiprocessing
GIL neboli Global Interpreter Lock je v podstatě mutex proti vykonávání vláken najednou.
V interpretru CPython (tj. interpret Python'u napsaný v C, navíc ten jediný „hlavní“), paměťová správa není thread-safe (jedno vlákno by mohlo rozbíjet paměť druhého) a proto vlákna musí být vykonávána sekvenčně. Tento problém řeší GIL – za cenu ztráty skutečného paralelismu pomocí vláken.
V jiných interpretrech Python'u (např. javovském Jythonu nebo C#-ovském IronPythonu) GIL není a je možno vlákna využít plně. V komunitě probíhá diskuse, zda by GIL neměl být z Python'u odstraněn. (Nutno ovšem podotknout, že tato diskuse probíhá prakticky po celou dobu existence GILu, takže…)
Jak už víme, modul subprocess
(konkrétně přes konstruktor Popen) umožňuje spuštění externího příkazu systému paralelně k běžícímu kódu v Python'u.
Kromě toho Popen umožňuje i jednoduchou synchronizaci:
V krajním případě by takto šel spustit i další pythonovský script, ale existují i lepší metody.
Příklad fork-bomby pomocí Popen
:
Paralelismus pomocí vláken zajišťuje modul threading
. Jsou v něm implementovány POSIXové synchronizační nástroje jako semafory, mutexy, bariéry a další. Má v podstatě stejné API jako modul multiprocessing
.
V rámci interpretru CPython ovšem zatím stále (květen 2012) nemá smysl a neočekává se, že by v blízké budoucnosti nějaký smysl měl (viz GIL).
Skutečný paralelismus v Python'u pomocí procesů zajišťuje modul multiprocessing
. Nové procesy přitom vytváří další instance pythonovského interpretru. Jednoduchý příklad:
os.fork()
. Bez této „ochrany“ se díky tomu totiž první proces stane nerozlišitelným od následujících a začnou se dít ošklivé věci.
Konstruktor Process
opravdu vytváří nový proces (v rámci OS), tzn. že spustí další interpret Python'u:
Něco zajímavějšího – ukázka, že se procesy skutečně vykonávají paralelně…
…na rozdíl od možnosti, kdy tentokrát sleep bude simulovat kritickou sekci a uzamkneme ho zámkem:
Protože ale máme procesy, které mají vlastní paměťové prostory, musíme mezi nimi konkrétní paměť sdílet:
0
, která je v každém procesu navýšena o náhodné číslo mezi 1 a 10. Jelikož každý proces se při svém spuštění uzamkne (acquire()), je sdílená hodnota navyšována postupně proces po procesu, jak jim odemčení předchozího procesu (release()) umožňuje pokračovat v práci.
Jak je vidět, paralelní programování v Pythonu je mnohem pohodlnější než v C/C++.
Přednášku pro vás připravil Jakub Tauchman.