2012-05-16 4 views
16

Ich bin den Python-Interpreter in eine Multithread-C-Anwendung einbetten und ich bin ein wenig verwirrt, welche APIs ich verwenden sollte, um Thread-Sicherheit zu gewährleisten.Einbetten von Python in Multithread-C-Anwendung

Nach dem, was ich gesammelt habe, ist es beim Einbetten von Python Sache des Embedders, sich um die GIL-Sperre zu kümmern, bevor irgendein anderer Python C-API-Aufruf aufgerufen wird. Dies wird mit diesen Funktionen getan:

gstate = PyGILState_Ensure(); 
// do some python api calls, run python scripts 
PyGILState_Release(gstate); 

Doch dies allein scheint nicht genug zu sein. Ich habe immer noch zufällige Abstürze, da es nicht mutual exclusion für den Python-APIs zur Verfügung zu stellen scheint.

Nach etwas mehr docs lese ich auch hinzugefügt:

PyEval_InitThreads(); 

direkt nach dem Aufruf Py_IsInitialized() aber das ist, wo der verwirrende Teil kommt. Die docs sagen, dass diese Funktion:

initialisieren und erwerben die globale Interpreter

sperren Dies deutet darauf hin, dass, wenn diese Funktion zurückkehrt, wird die GIL soll gesperrt werden und sollte irgendwie freigeschaltet werden. aber in der Praxis scheint dies nicht erforderlich zu sein. Mit dieser Linie an Ort und Stelle war mein multithreaded perfekt und gegenseitiger Ausschluß wurde von den PyGILState_Ensure/Release Funktionen beibehalten.
Als ich versuchte, PyEval_ReleaseLock() nach PyEval_ReleaseLock() hinzuzufügen, wurde die App ziemlich schnell in einem nachfolgenden Anruf zu PyImport_ExecCodeModule() dead-locked.

Was fehle ich hier?

Antwort

4

Schließlich habe ich es herausgefunden.
Nach

PyEval_InitThreads(); 

Sie benötigen

PyEval_SaveThread(); 

nennen Während richtig die GIL für den Haupt-Thread freizugeben.

+0

Dies ist falsch und möglicherweise schädlich: 'PyEval_SaveThread' sollte immer in Verbindung mit' PyEval_RestoreThread' stehen. Wie [an anderer Stelle erläutert] (http://stackoverflow.com/a/15471525/1600898) sollten Sie nicht versuchen, die Sperre nach der Initialisierung zu lösen; Überlassen Sie es Python, um es als Teil seiner regulären Arbeit zu veröffentlichen. – user4815162342

+0

Ich sehe nicht, warum es schädlich ist, wenn Sie alle Aufrufe an Python in einem _Block _ _Allow_ Blöcke setzen. Wenn Sie andererseits 'PyEval_SaveThread();' nicht aufrufen, blockiert Ihr Haupt-Thread den Zugriff anderer Threads auf Python. Mit anderen Worten: "PyGILState_Ensure()" Deadlocks. – khkarens

+0

Dies ist die einzige Sache, die sowohl für das Einbetten von Python als auch das Aufrufen eines Erweiterungsmoduls funktioniert. –

-1

Eine multi-threaded C-App zu haben, die versucht, von mehreren Threads zu mehreren Python-Threads einer einzelnen CPython-Instanz zu kommunizieren, sieht für mich riskant aus.

Solange nur ein C-Thread mit Python kommuniziert, sollten Sie sich nicht um Sperren kümmern müssen, auch wenn die Python-Anwendung Multi-Threading ist. Wenn mehrere Threads Python benötigen können Sie die Anwendung auf diese Weise eingerichtet und mehr C Threads über eine Warteschlange mit diesem einzelnen Thread C kommunizieren, die sie aus, um mehr Threads Python Farmen.

Eine Alternative, die für Sie arbeiten könnte, ist mehrere CPython-Instanzen eine für jeden C-Thread, der es benötigt (natürlich Kommunikation zwischen Python-Programmen sollte über das C-Programm sein).

Eine weitere Alternative könnte der Stackless Python Interpreter sein. Das tut mit dem GIL weg, aber ich bin nicht sicher, ob Sie laufen in andere Probleme, die es zu mehreren Threads zu binden. stackless war ein Ersatz für meine (single-threaded) C-Anwendung.

+1

Sie taten Ihr Bestes, um die Frage nicht wirklich zu beantworten. Ich bin nicht daran interessiert, die Arbeit an einen einzigen Thread zu hängen. – shoosh

5

Ich hatte genau das gleiche Problem und es ist jetzt gelöst, indem Sie PyEval_SaveThread() sofort nach PyEval_InitThreads() verwenden, wie Sie oben vorschlagen.Mein aktuelles Problem war jedoch, dass ich PyEval_InitThreads() nach PyInitialise() verwendet, was dann dazu führte, dass PyGILState_Ensure() blockierte, wenn es von verschiedenen, nachfolgenden nativen Threads aufgerufen wurde. Zusammenfassend ist es das, was ich jetzt tun:

  1. global Es Variable ist:

    static int gil_init = 0; 
    
  2. Von einem Hauptthread der native C-Erweiterung laden und die Python-Interpreter starten:

    Py_Initialize() 
    
  3. Aus mehreren anderen Threads ruft meine App gleichzeitig viele Aufrufe in die Python/C-API auf:

  4. Vom Haupt-Thread stoppen den Python-Interpreter

    Py_Finalize() 
    

Alle anderen Lösungen, die ich versucht habe, entweder verursachte zufälligen Python sigfaults oder Deadlock/Sperrung PyGILState_Ensure() verwenden.

Die Python-Dokumentation sollte hier wirklich klarer sein und zumindest ein Beispiel für die Anwendungsfälle Einbettung und Erweiterung bieten.