Python hat in letzter Zeit viel Aufmerksamkeit erhalten. Mit der Veröffentlichung von 3.13, die für Oktober dieses Jahres geplant ist, beginnt die gewaltige Arbeit zur Entfernung der GIL. Für neugierige Benutzer, die ein (fast) GIL-freies Python ausprobieren möchten, ist bereits eine Vorabversion verfügbar.
Dieser ganze Hype hat mich dazu gebracht, mich mit meiner eigenen Sprache, ArkScript, zu beschäftigen, da ich in der Vergangenheit auch eine globale VM-Sperre hatte (hinzugefügt in Version 3.0.12 im Jahr 2020, entfernt in 3.1.3 im Jahr 2022). Vergleichen Sie die Dinge und zwingen Sie mich, tiefer in das Wie und Warum der Python-GIL einzutauchen.
Eine globale Interpretersperre (GIL) ist ein Mechanismus, der in Computerspracheninterpretern verwendet wird, um die Ausführung von Threads zu synchronisieren, sodass nur ein nativer Thread (pro Prozess) grundlegende Operationen (wie Speicherzuweisung und Referenzzählung) gleichzeitig ausführen kann Zeit.
Wikipedia – Globale Interpretersperre
Parallelität liegt vor, wenn zwei oder mehr Aufgaben in überlappenden Zeiträumen starten, ausgeführt und abgeschlossen werden können, das bedeutet jedoch nicht, dass sie beide gleichzeitig ausgeführt werden.
Parallelität liegt vor, wenn Aufgaben buchstäblich gleichzeitig ausgeführt werden, z. B. auf einem Multicore-Prozessor.
Eine ausführliche Erklärung finden Sie in dieser Stack Overflow-Antwort.
Die GIL kann die Geschwindigkeit von Single-Thread-Programmen erhöhen, da Sie nicht alle Datenstrukturen sperren und freigeben müssen: Der gesamte Interpreter ist gesperrt, sodass Sie standardmäßig sicher sind.
Da es jedoch eine GIL pro Interpreter gibt, schränkt dies die Parallelität ein: Sie müssen einen völlig neuen Interpreter in einem separaten Prozess erzeugen (unter Verwendung des Multiprocessing-Moduls anstelle von Threading), um mehr als einen Kern zu verwenden! Dies ist mit höheren Kosten verbunden als nur das Erzeugen eines neuen Threads, da Sie sich jetzt um die Kommunikation zwischen Prozessen kümmern müssen, was einen nicht zu vernachlässigenden Overhead mit sich bringt (Benchmarks finden Sie unter „GeekPython – GIL Become Optional in Python 3.13“).
Im Fall von Python liegt es daran, dass die Hauptimplementierung, CPython, über keine threadsichere Speicherverwaltung verfügt. Ohne die GIL würde das folgende Szenario eine Race-Bedingung erzeugen:
Wenn Thread 1 zuerst ausgeführt wird, beträgt die Anzahl 11 (Anzahl * 2 = 10, dann Anzahl + 1 = 11).
Wenn Thread 2 zuerst ausgeführt wird, beträgt die Anzahl 12 (Anzahl + 1 = 6, dann Anzahl * 2 = 12).
Die Reihenfolge der Ausführung ist wichtig, aber es kann noch schlimmer passieren: Wenn beide Threads gleichzeitig count lesen, löscht einer das Ergebnis des anderen und count beträgt entweder 10 oder 6!
Insgesamt macht eine GIL die (CPython-)Implementierung im Allgemeinen einfacher und schneller:
Außerdem wird das Umschließen von C-Bibliotheken einfacher, da Ihnen dank der GIL Thread-Sicherheit garantiert ist.
Der Nachteil ist, dass Ihr Code asynchron ist, wie in gleichzeitig, aber nicht parallel.
[!NOTE]
Python 3.13 entfernt die GIL!Der PEP 703 hat eine Gebäudekonfiguration hinzugefügt –disable-gil, sodass Sie bei der Installation von Python 3.13+ von Leistungsverbesserungen in Multithread-Programmen profitieren können.
In Python müssen Funktionen eine Farbe annehmen: Sie sind entweder „normal“ oder „asynchron“. Was bedeutet das in der Praxis?
>>> def foo(call_me): ... print(call_me()) ... >>> async def a_bar(): ... return 5 ... >>> def bar(): ... return 6 ... >>> foo(a_bar) <coroutine object a_bar at 0x10491f480> <stdin>:2: RuntimeWarning: coroutine 'a_bar' was never awaited RuntimeWarning: Enable tracemalloc to get the object allocation traceback >>> foo(bar) 6
Da eine asynchrone Funktion nicht sofort einen Wert zurückgibt, sondern eine Coroutine aufruft, können wir sie nicht überall als Rückrufe verwenden, es sei denn, die von uns aufgerufene Funktion ist für die Annahme asynchroner Rückrufe ausgelegt.
Wir erhalten eine Hierarchie von Funktionen, da „normale“ Funktionen asynchron gemacht werden müssen, um das Schlüsselwort „await“ zu verwenden, das zum Aufrufen asynchroner Funktionen benötigt wird:
can call normal -----------> normal can call async -+-----------> normal | .-----------> async
Abgesehen davon, dass man dem Aufrufer vertraut, gibt es keine Möglichkeit herauszufinden, ob ein Rückruf asynchron ist oder nicht (es sei denn, man versucht, ihn zuerst innerhalb eines Try/Except-Blocks aufzurufen, um nach einer Ausnahme zu suchen, aber das ist hässlich).
In the beginning, ArkScript was using a Global VM Lock (akin to Python's GIL), because the http.arkm module (used to create HTTP servers) was multithreaded and it caused problems with ArkScript's VM by altering its state through modifying variables and calling functions on multiple threads.
Then in 2021, I started working on a new model to handle the VM state so that we could parallelize it easily, and wrote an article about it. It was later implemented by the end of 2021, and the Global VM Lock was removed.
ArkScript does not assign a color to async functions, because they do not exist in the language: you either have a function or a closure, and both can call each other without any additional syntax (a closure is a poor man object, in this language: a function holding a mutable state).
Any function can be made async at the call site (instead of declaration):
(let foo (fun (a b c) (+ a b c))) (print (foo 1 2 3)) # 6 (let future (async foo 1 2 3)) (print future) # UserType<0, 0x0x7f0e84d85dd0> (print (await future)) # 6 (print (await future)) # nil
Using the async builtin, we are spawning a std::future under the hood (leveraging std::async and threads) to run our function given a set of arguments. Then we can call await (another builtin) and get a result whenever we want, which will block the current VM thread until the function returns.
Thus, it is possible to await from any function, and from any thread.
All of this is possible because we have a single VM that operates on a state contained inside an Ark::internal::ExecutionContext, which is tied to a single thread. The VM is shared between the threads, not the contexts!
.---> thread 0, context 0 | ^ VM <----+ can't interact | v .---> thread 1, context 1
When creating a future by using async, we are:
This forbids any sort of synchronization between threads since ArkScript does not expose references or any kind of lock that could be shared (this was done for simplicity reasons, as the language aims to be somewhat minimalist but still usable).
However this approach isn't better (nor worse) than Python's, as we create a new thread per call, and the number of threads per CPU is limited, which is a bit costly. Luckily I don't see that as problem to tackle, as one should never create hundreds or thousands of threads simultaneously nor call hundreds or thousands of async Python functions simultaneously: both would result in a huge slow down of your program.
In the first case, this would slowdown your process (even computer) as the OS is juggling to give time to every thread ; in the second case it is Python's scheduler that would have to juggle between all of your coroutines.
[!NOTE]
Out of the box, ArkScript does not provide mechanisms for thread synchronization, but even if we pass a UserType (which is a wrapper on top of type-erased C++ objects) to a function, the underlying object isn't copied.
With some careful coding, one could create a lock using the UserType construct, that would allow synchronization between threads.(let lock (module:createLock)) (let foo (fun (lock i) { (lock true) (print (str:format "hello {}" i)) (lock false) })) (async foo lock 1) (async foo lock 2)Nach dem Login kopieren
ArkScript and Python use two very different kinds of async / await: the first one requires the use of async at the call site and spawns a new thread with its own context, while the latter requires the programmer to mark functions as async to be able to use await, and those async functions are coroutines, running in the same thread as the interpreter.
Originally from lexp.lt
Das obige ist der detaillierte Inhalt vonVergleich der asynchronen Modelle Python und ArkScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!