Dit is een samenvatting van mijn PyCon 2020-tutorial. U kunt de originele video en de broncode hier vinden: https://github.com/santiagobasulto/pycon-concurrency-tutorial-2020
Dit is een snelle gids/tutorial over hoe je effectief gelijktijdige programma’s kunt schrijven met Python. Concurrency in Python kan verwarrend zijn. Er zijn meerdere modules (threading, _thread, multiprocessing, subprocess). Er is ook de zo gehate GIL, maar alleen voor CPython (PyPy en Jython hebben geen GIL). Collecties zijn niet thread safe, behalve voor enkele implementatiedetails met CPython.
Het doel van deze tutorial is om nuchtere aanbevelingen te geven over hoe je elk gebruik van gelijktijdige (of parallelle) code benadert.
Je zult merken dat, in tegenstelling tot sommige andere meer traditionele “multithreading only” Python tutorials, ik begin met een samenvatting van zowel Computer Architectuur als concepten van Besturingssystemen. Het begrijpen van deze basis concepten is fundamenteel voor het correct bouwen van concurrent programma’s. Sla deze secties over als je al bekend bent met deze onderwerpen.
Deze tutorial is onderverdeeld in de volgende secties:
Concurrency vs Parallelism
Computer Architectuur
De rol van het Operating System
Threads met Python
Thread Synchronisatie
Multiprocessing
High level libraries: concurrent.futures en parallel
Concurrency vs Parallelism
Parallelisme is wanneer meerdere taken tegelijkertijd worden uitgevoerd. Het is het uiteindelijke doel van parallelle programma’s. Concurrency is minder dan parallellisme, het betekent dat we verschillende taken starten en ze in dezelfde tijd laten lopen. Maar op een bepaald moment doen we er maar één tegelijk.
Toegepast op koken, zouden dit voorbeelden zijn van concurrency en parallellisme.
Concurrency heeft slechts één persoon in de keuken:
Start met het snijden van de uien
Start met het verwarmen van de pan
Voltooi het snijden van de uien
In dit geval is het duidelijk dat we niet meerdere dingen tegelijk kunnen doen. We kunnen ze allemaal beginnen, maar we moeten heen en weer springen om ze te beheersen.
Parallelisme heeft meerdere mensen in de keuken:
Persoon 1 snijdt uien
Persoon 2 snijdt rode paprika
Persoon 3 wacht tot de pan is opgewarmd
In dit geval worden er meerdere taken tegelijk gedaan.
Computerarchitectuur
Alle moderne computers kunnen worden vereenvoudigd met behulp van de von Neumann-architectuur, die uit 3 hoofdonderdelen bestaat: Rekenen (CPU), Geheugen (RAM), I/O (harde schijven, netwerken, video-uitgang).
Uw code zal altijd taken uitvoeren die met elk van deze componenten te maken hebben. Sommige instructies zijn “computationeel” ( x + 1), sommige zijn geheugen (x = 1), en sommige zijn I/O (fp = open('data.csv')).
Om de details van concurrency in Python goed te begrijpen, is het belangrijk om de verschillende “toegangstijden” van deze componenten in gedachten te houden. Toegang tot de CPU is een stuk sneller dan toegang tot RAM, laat staan I/O. Er is een studie die computerlatentie vergelijkt met menselijke relatieve tijden: als een CPU-cyclus 1 seconde menselijke tijd vertegenwoordigt, duurt een netwerkverzoek van San Francisco naar Hong Kong 11 jaar.
Dit is de sleutel tot het tegengaan van de beperking van de GIL. Op het moment dat een thread iets uit een bestand moet lezen, kan deze de CPU vrijgeven en andere threads laten draaien. Het zal veel tijd kosten totdat de harde schijf de gegevens teruggeeft. Dit komt omdat de meeste programma’s wat we noemen I/O-gebonden zijn. Dat wil zeggen, ze gebruiken veel I/O-functies (netwerk, bestandssysteem, etc).
Je kunt een “bewijs” zien van een I/O-gebonden taak in het notebook: 5. De Python GIL.ipynb.
Multiprocessing
Sommige andere programma’s zijn onvermijdelijk CPU-gebonden, zoals programma’s die veel berekeningen moeten doen. Soms wil je dingen versnellen. Als je multithreading gebruikt voor CPU-gebonden taken, zul je merken dat je programma’s niet sneller worden door de GIL (ze kunnen zelfs langzamer worden).
Enterter multiprocessing, het tweede mechanisme dat we hebben om gelijktijdige programma’s te schrijven. In plaats van meerdere threads, spawnen we meerdere kind-processen die verschillende taken gelijktijdig uitvoeren.
Er staat een voorbeeld van een op multiprocessing gebaseerd programma in notebook 6. Multiprocessing.ipynb
concurrent.futures
Om mijn tutorial af te ronden, wil ik erop wijzen dat er een module van een hoger niveau is, die deel uitmaakt van de Python Standaard Bibliotheek, die indien mogelijk gebruikt zou moeten worden: concurrent.Futures.
Het biedt abstracties op hoog niveau om thread- en process pools te maken, en het is volwassen genoeg om te worden beschouwd, waar mogelijk, als het standaard alternatief om concurrent code te schrijven.
Daar heb je het! Laat het me weten als je nog vragen hebt. Ik beantwoord ze graag.