11.1 Global Interpreter Lock
Global Interpreter Lock (GIL)
— das ist ein Mechanismus im CPython-Interpreter, der sicherstellt, dass nur ein Thread den Python-Bytecode zu einem gegebenen Zeitpunkt ausführt. GIL
verhindert die parallele Ausführung von Threads, was die Leistung von Multithreading-Programmen negativ beeinflussen kann, insbesondere auf Mehrkernprozessoren.
Gründe für die Existenz von GIL
Vereinfachung der Speicherverwaltung: GIL
vereinfacht die Speicherverwaltung und die Garbage Collection, wodurch Python einfacher zu implementieren ist.
Thread-Sicherheit: GIL
verhindert Race Conditions, wodurch die Codeausführung ohne die Notwendigkeit von Sperrungen thread-sicher wird.
Probleme, die durch GIL
verursacht werden
Begrenzte Leistung: Multithreading- Programme, die rechenintensive Aufgaben ausführen, können die Vorteile von Mehrkernprozessoren aufgrund der Einschränkungen von GIL
nicht vollständig nutzen.
Verzerrte Leistung: Programme, die Threads intensiv nutzen, um Aufgaben auszuführen, können eine Leistungseinbuße aufgrund des Kontextwechsels zwischen Threads erleben.
Derzeit gibt es vier Hauptmethoden zur „Umgehung des GIL“:
11.2 Nutzung von Multi-Processing (multiprocessing)
Das Modul multiprocessing
ermöglicht die Erstellung von Prozessen, die parallel ausgeführt werden und nicht durch GIL
eingeschränkt sind, da jeder Prozess seinen eigenen Python-Interpreter und Speicher hat.
Beispiel:
import multiprocessing
def worker(num):
print(f'Worker: {num}')
def main():
processes = []
for i in range(5):
p = multiprocessing.Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
main()
11.3 Asynchrones Programmieren
Asynchrones Programmieren mit asyncio
erlaubt die parallele Ausführung von Aufgaben, ohne den Haupt-Thread zu blockieren. Obwohl dies nicht direkt GIL
umgeht, ermöglicht es die effektive Nutzung von Wartezeiten zur Ausführung anderer Aufgaben.
Beispiel:
import asyncio
async def main():
await asyncio.sleep(1)
print('Hallo')
asyncio.run(main())
11.4 Verwendung von Bibliotheken mit eigenem Thread-Management
Einige Bibliotheken, wie NumPy und SciPy, sind in C geschrieben und verwenden eigene Mechanismen zur Thread-Verwaltung, was es ihnen ermöglicht, GIL zu umgehen und Mehrkernprozessoren für Berechnungen effizient zu nutzen.
Tatsächlich ist dies einer der Hauptgründe für den Erfolg von Python, wie langsam es auch sein mag. Alle komplexen Berechnungen sind in C/C++ neu geschrieben und werden auf allen Prozessorkernen oder sogar direkt auf den Kernen der Grafikkarte ausgeführt. Und auf modernen Grafikkarten gibt es tausende von Kernen.
Es stellt sich heraus, dass es nicht mehr wichtig ist, wie schnell oder langsam die Sprache ist, wenn alle ressourcenintensiven Berechnungen von externen Bibliotheken ausgeführt oder sogar in entfernte Rechenzentren verlagert werden.
11.5 Ausführung von Berechnungen außerhalb des Python-Interpreters
Nutzung von Erweiterungen in C/C++ oder anderen Sprachen, die Berechnungen außerhalb des Python-Interpreters ausführen können und dann das Ergebnis zurückgeben. Dies ermöglicht das Vermeiden der GIL-Blockierung während der Ausführung von intensiven Berechnungen.
Beispiel für die Nutzung von Cython:
# example.pyx
def compute(int n):
cdef int i, result = 0
for i in range(n):
result += i
return result
Kompilierung und Nutzung:
cythonize -i example.pyx
import example
print(example.compute(1000000))
GO TO FULL VERSION