PyEval_InitThreads in Python 3: Wie/Wann Sie es nennen? (die saga geht weiter ad nauseam)
Grundsätzlich scheint es massive Verwirrung/Unklarheit über genau, Wann PyEval_InitThreads()
soll aufgerufen werden, und welche begleitenden API-Aufrufe benötigt werden. Die offizielle Python Dokumentation ist leider sehr mehrdeutig. Es gibt bereits viele Fragen auf stackoverflow zu diesem Thema, und in der Tat, ich habe persönlich schon auf eine Frage fast identisch, damit es mich nicht besonders Wundern, wenn diese geschlossen ist, als eine doppelte; aber Bedenken Sie, dass es scheint keine definitive Antwort auf diese Frage. (Leider habe ich nicht Guido Van Rossum auf speed-dial.)
Erstens, wir definieren den Umfang der Frage hier: was will ich tun? Naja... ich will schreiben Sie ein Python-Erweiterungsmodul in C, der:
- Spawn-worker-threads mit der
pthread
- API in C - Aufrufen von Python-callbacks innerhalb dieser C-threads
Okay, also lasst uns beginnen mit der Python-docs selbst. Die Python 3.2 docs sagen:
void PyEval_InitThreads()
Initialisieren und dem Erwerb der global interpreter lock. Es sollte
Aufruf in der main-thread vor dem erstellen Sie einen zweiten thread oder sich
in einem anderen thread-Operationen, wie PyEval_ReleaseThread(tstate).
Es ist nicht erforderlich, vor dem Aufruf PyEval_SaveThread() oder
PyEval_RestoreThread().
Also mein Verständnis ist, dass:
- Jede C-extension-Modul, welches threads erstellt, muss anrufen
PyEval_InitThreads()
aus dem Haupt-thread, bevor irgendwelche anderen threads
hervorgebracht - Aufrufen
PyEval_InitThreads
sperrt die GIL
Also der gesunde Menschenverstand würde uns sagen, dass jede C-extension-Modul, welches threads erstellt, muss call PyEval_InitThreads()
, und lassen Sie dann die Globale Interpreter Lock. Okay, scheint einfach genug. So prima-facie -, alles, was erforderlich sein würde, den folgenden code:
PyEval_InitThreads(); /* initialize threading and acquire GIL */
PyEval_ReleaseLock(); /* Release GIL */
Scheint einfach genug... aber leider, die Python 3.2 docs auch sagen, dass PyEval_ReleaseLock
wurde nicht. Stattdessen sollten wir nutzen, PyEval_SaveThread
zur Freigabe des GIL:
PyThreadState* PyEval_SaveThread()
Release der global interpreter lock (wenn es erstellt wurde und Faden
- Unterstützung aktiviert ist) und setzen Sie die thread-Zustand auf NULL, Rücksendung der
vorherigen thread-Zustand (was nicht NULL ist). Wenn die Sperre
erstellt, die den aktuellen thread muss es erworben haben.
Ähm... okay, also ich denke eine C-extension-Modul muss sagen:
PyEval_InitThreads();
PyThreadState* st = PyEval_SaveThread();
In der Tat, das ist genau das, was diese stackoverflow-Antwort sagt. Außer, wenn ich eigentlich versuchen dies in der Praxis, den Python-interpreter sofort seg-Fehler beim importieren der extension-Modul. Schön.
Okay, so, jetzt ich ' m aufgeben auf der offiziellen Python-Dokumentation und wandte sich an Google. So, dieser random blog behauptet, alle Sie tun müssen, um aus einer Erweiterungs-Modul ist der Aufruf PyEval_InitThreads()
. Natürlich, die Dokumentation behauptet, dass PyEval_InitThreads()
erwirbt der GIL, und in der Tat, ein PyEval_InitThreads()
in ceval.c
take_gil(PyThreadState_GET());
So PyEval_InitThreads()
definitiv erwirbt der GIL. Ich würde denken dann, dass Sie unbedingt brauchen, um irgendwie lassen Sie die GIL nach dem Aufruf PyEval_InitThreads()
. Aber wie? PyEval_ReleaseLock()
ist veraltet, und PyEval_SaveThread()
einfach unerklärlich seg-Fehler.
Okay... also vielleicht für einige Grund, die derzeit jenseits meines Verständnisses, eine C-extension module nicht müssen Freisetzung der GIL. Das habe ich versucht... und, wie erwartet, sobald ein anderer thread versucht zu erwerben GIL (mit PyGILState_Ensure), das Programm hängt an einem deadlock. Also ja... Sie wirklich freigeben müssen, die GIL nach dem Aufruf PyEval_InitThreads()
.
Also wieder, die Frage ist: wie du loslassen GIL nach dem Aufruf PyEval_InitThreads()
?
Und generell: was genau ist eine C-extension-Modul muss in der Lage sein, um sicher zu aufrufen von Python-code vom Arbeiter C-threads?
- Verwandte: Python-code aufruft, der C-Bibliothek, die das erstellen von Betriebssystem-threads, die dann irgendwann Aufruf von Python-callbacks. Siehe Beispiel
c_extension
Modul gibt (Ihr Zweck ist, einen Fehler auslöst, inthreading
während Sie "richtig" zu Belichten einen Fehler inthreading
Umsetzung. Es versäumt, einen Fehler auslöst, auf Python 3). - Hast du es geschafft, dies zu lösen? Ich habe genau das gleiche problem und meiner c-Anwendung hält Sie geben segfaults egal was ich mache
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ihr Verständnis ist korrekt: aufrufen
PyEval_InitThreads
hat, unter anderem, erwerben die GIL. In einem korrekt geschriebenen Python/C Anwendung ist, ist dies kein Problem, da die GIL wird freigeschaltet in der Zeit, entweder automatisch oder manuell.Wenn der Haupt-thread geht auf ausführen Python-code, gibt es nichts besonderes zu tun, weil Python-interpreter automatisch verzichten die GIL nach einer Reihe von Anweisungen, die ausgeführt wurden (so dass ein anderer thread, es zu erwerben,, das wird aufgeben es wieder, und so weiter). Zusätzlich, wenn Python ist zu berufen, ein blockierender Systemaufruf, z.B. Lesen von der Netzwerk oder in eine Datei schreiben, wird es lassen Sie die GIL um den Ruf.
Die original-version von dieser Antwort ziemlich viel endete hier. Aber es gibt eine weitere Sache zu berücksichtigen: die Einbettung Szenario.
Wenn das einbetten von Python, der Haupt-thread oft initialisiert Python und geht zum ausführen von anderen, nicht-Python-bezogenen Aufgaben. In diesem Szenario gibt es nichts, was automatisch release der GIL, so muss dies getan werden, indem der thread selbst. Das ist in keiner Weise spezifisch für den Anruf, die Anrufe
PyEval_InitThreads
es wird erwartet, dass der alle Python/C-code aufgerufen wird, mit der GIL erworben.Beispielsweise die
main()
enthält möglicherweise code wie folgt:Wenn Ihr code erstellt threads manuell, die Sie benötigen, zu erwerben, die GIL vor alles Python-bezogenen, auch so einfach wie
Py_INCREF
. Verwenden Sie dazu die folgenden:PyGILState_Ensure()/PyGILState_Release()
sollte verwendet werden, zum aufrufen von Python aus C-thread stattPyEval_{Save,Restore}Thread()
Finden Sie unter Thread-Zustand und die Globale Interpreter-SperrePyEval_InitThreads()
dann, wenn ein worker-thread ruftPyGILState_Ensure
, es geht in die Sackgasse.PyEval_InitThreads
zufällig. In der Tat, die Antwort empfiehlt explizit zu nennenPyEval_InitThreads
in der Erweiterung<module>init
. Wenn dies eingehalten wird, wird die init-Funktion wird wieder der Python Anrufer, dass der Auslöser für den import. Python wird munter weiter auf dem Weg, schließlich die Freigabe der GIL, wie beschrieben in der Antwort im detail.PyGILState_Ensure()
, die versucht, zum abrufen von GIL, WIRD deadlock (genauer gesagt, stecken in einer unendlichen Schleife innerhalb der Funktiontake_gil()
in der Python-source-code), wenn der GIL ist nicht auffindbar. Um es retreivable, nach einem AufrufPyEval_InitThreads()
der code die Funktion aufrufen, müssen auch call -PyEval_SaveThread()
. (AußerdemPyEval_RestoreThread()
wird immer wieder die GIL nicht für den Abruf durchPyGILState_Ensure()
.)PyEval_InitThreads
ist offensichtlich Python-bewusst. So der thread wird entweder halten Sie Python, die Folge in der GIL veröffentlicht wird früher oder später (durch die interpreter-Schleife oder durch eine blocking-IO Aufruf von Python); oder es wird der Aufruf in C-code, in dem Fall ist es seine Verantwortung zur Freigabe der GIL, bevor Sie dies tun (und erneut es danach). Ehrlich, ich verstehe nicht, wo die Verwirrung liegt - wenn ich es Tue, werde ich die änderung der Antwort, um es anzugehen.PyGILState_Ensure()
aus einem thread gestartet aus der C++ - code stecken in einer Endlosschleife versuchen, zu erwerben, die GIL.Habe ich gesehen, Symptome ähnlich wie bei Ihnen: deadlocks, wenn ich nur call PyEval_InitThreads(), weil mein Haupt-thread ruft nie etwas von Python wieder, und Zugriffsfehler, wenn ich unbedingt rufen Sie so etwas wie PyEval_SaveThread(). Die Symptome hängen von der version von Python und auf die situation: ich entwickle ein plug-in bettet Python für eine Bibliothek, die geladen werden können, als Teil eines Python-Erweiterung. Der code muss daher ausgeführt werden, unabhängig davon, ob es geladen wird Python als main.
Folgenden gearbeitet werden, sowohl mit python2.7 und Python ist3.4, und mit meiner Bibliothek bei der Ausführung in Python und außerhalb von Python. In meinem plug-in init-routine, die ausgeführt wird, in der Haupt-thread, ich führe:
(mainPyThread ist tatsächlich eine statische variable, aber ich glaube nicht, dass die Sachen, wie ich Sie nie brauchen, um es wieder verwenden).
Dann erstelle ich threads mit pthreads, und in jeder Funktion, die Zugriff auf die Python-API, die ich verwenden:
PyEval_RestoreThread(mainPyThread)
irgendwo vorPy_FinalizeEx()
. Stellen Sie sicher, zu warten, bis alle threads beendet haben, Ihre Ausführung bevor Sie dies tun, obwohl, oder jeden Anruf anPyGILState_Ensure()
in jedem thread, deadlock (Endlosschleife innerhalb der Funktiontake_gil()
in der Python-source-code), weil die GIL ist jetzt verfügbar wieder.PyEval_InitThreads
wirklich nicht alles tun, Python-Zusammenhang für den rest seines Lebens, dann ist es wird richtig sofort anrufenPyEval_SaveThread
, und nur callPyEval_RestoreThread
beim Herunterfahren. Ich habe eine endgültige nit - könnten Sie missbrauchen den Begriff "deadlock", was bedeutet "warten auf eine Bedingung, die niemals erfüllt werden können".take_gil
wird in der Tat gelingt, nehmen die GIL, sobald es zur Verfügung gestellt wird.Gibt es zwei Methoden der multi-threading, während der Ausführung der C/Python API.
1.Die Ausführung der verschiedenen threads mit den gleichen interpreter - Wir führen einen Python-interpreter und teilen die gleichen Dolmetscher über die verschiedenen threads.
Die Codierung wird wie folgt sein.
Den code wie folgt
Ist es notwendig zu beachten, dass die Globale Interpreter-Sperre noch fortbesteht, und trotz der individuellen Interpreten zu jedem thread, wenn es um python-Ausführung, können wir noch ausführen nur ein thread zu einem Zeitpunkt. GIL ist EINZIGARTIGE zu PROZESS, so dass trotz der Bereitstellung von einzigartigen sub-interpreter zu jedem thread, wir können nicht gleichzeitige Ausführung von threads
Quellen: Ausführen eines Python-interpreter in den Haupt-thread und zu jedem thread den wir geben können, seine eigene sub-interpreter
Multi-threading-tutorial (msdn)
Den Vorschlag zu nennen PyEval_SaveThread funktioniert
Jedoch um einen Absturz zu verhindern als Modul importiert wird, sicherzustellen Python-APIs zu importieren sind geschützt, mit
PyGILState_Ensure und PyGILState_Release
z.B.
Zitat oben:
Nun, für eine längere Antwort:
Ich bin beschränken sich meine Antwort werden über Python-Erweiterungen (im Gegensatz zum einbetten von Python). Wenn wir nur die Ausweitung Python, als ein beliebiger Einstiegspunkt in das Modul von Python. Diese per definition bedeutet, dass wir nicht haben, um sorgen über den Aufruf einer Funktion aus einer nicht-Python-Kontext, das macht die Sache ein bisschen einfacher.
Wenn threads NICHT initialisiert werden, dann wissen wir, es gibt keine GIL (keine threads == keine Notwendigkeit für sperren), und damit "Es ist nicht sicher, um diese Funktion aufrufen, wenn es unbekannt ist, welcher thread (wenn überhaupt) derzeit hat die Globale interpreter-Sperre" gilt nicht.
Nach dem Aufruf PyEval_InitThreads(), a GIL erstellt und zugewiesen... zu unserem thread, das ist der thread aktuell ausgeführten Python-code. Also alles ist gut.
Nun, so weit wie unsere eigenen gestarteten worker "C"-threads, die Sie benötigen, zu Fragen, für die GIL vor der Ausführung relevanten code ein: so Ihre gemeinsame Methode ist, wie folgt:
Wir nicht haben, um sorgen über Deadlocks mehr, als von einer normalen Nutzung von Erweiterungen. Wenn wir in unsere Funktion, wir hatten die Kontrolle über Python, also entweder wir waren nicht unter Verwendung von Fäden (also kein GIL), oder die GIL war bereits jetzt an uns abgetreten. Wenn wir die Kontrolle zurück in die Python-Laufzeit durch verlassen unserer Funktion, der normalen Verarbeitung-Schleife prüft die GIL und Kontrolle über die als angemessen, andere anfordernde Objekte: darunter unser worker-threads über PyGILState_Ensure().
All dies dem Leser wahrscheinlich schon weiß. Aber der "Beweis ist in den pudding". Ich habe eine sehr-minimal-Beispiel dokumentiert, dass ich schrieb, die heute zu lernen für mich, was das Verhalten eigentlich war, und dass alles richtig funktioniert. Beispiel-Source-Code auf GitHub
Lernte ich einige Dinge mit dem Beispiel CMake-integration mit Python-Entwicklungs -, SCHLUCK-integration mit den beiden oben genannten, und Python Verhaltensweisen, die mit Erweiterungen und threads. Dennoch, der Kern der Beispiel können Sie:
... und das alles ohne Abstürze oder Zugriffsfehler. Zumindest auf meinem system (Ubuntu Linux w/GCC).
Brauchen Sie nicht zu nennen, die in Ihrem Erweiterung Module. Das ist für das initialisieren der interpreter, der bereits getan hat, wenn Ihre C-API-Erweiterung-Modul importiert wird. Diese Schnittstelle wird verwendet werden durch die Einbettung von Anwendungen.
Wann ist PyEval_InitThreads soll aufgerufen werden?
PyEval_InitThreads
wenn Sie planen, zu tun Python-callbacks von mehreren nicht-Python-threads (wie die Antwort, die du verlinkt hast sagt)Ich das Gefühl zu verwirren, auf dieses Problem zu. Der folgende code funktioniert, durch Zufall.
Mein Haupt-thread einige python-Laufzeitumgebung ersten arbeiten, und erstellen Sie andere pthread zu erledigen. Und ich habe eine bessere Lösung für dieses. In der Main-thread: