2009-09-15 14 views
9

Si tratta di un follow-up a Call Python from C++Calling Py_Finalize() da C

Al di avvio del programma che io chiamo la seguente funzione per inizializzare l'interprete:

void initPython(){ 
    PyEval_InitThreads(); 
    Py_Initialize(); 
    PyEval_ReleaseLock(); 
} 

Ogni thread crea il proprio struttura di dati e acquisisce la serratura con:

PyGILState_STATE gstate; 
gstate = PyGILState_Ensure(); 
//call python API, process results 
PyGILState_Release(gstate); 

Piuttosto semplice, una volta che hai capito la GIL, ma il problema è che ho un segfault quando si chiama Py_Finalize().

void exitPython(){ 
    PyEval_AcquireLock(); 
    Py_Finalize(); 
} 

il riferimento è piuttosto dubbioso sul Py_Finalize() (o forse sto solo leggendo nel modo sbagliato) e non sono sicuro se PyEval_AcquireLock() può acquisire il blocco se ci sono alcuni thread attivi e cosa succede se ci sono thread attivi quando viene chiamato Py_Finalize().

Ad ogni modo, ottengo un segfault anche se sono sicuro che tutti i thread hanno terminato il loro lavoro, ma solo se ne è stato creato almeno uno. Per esempio. chiamare initPython() seguito da exitPython() non crea alcun errore.

ho potuto solo ignorare il problema e spero che il sistema operativo sa quello che fa, ma mi piacerebbe prefere se riuscivo a capire cosa sta succedendo ..

risposta

7

Sì, l'intera sezione è piuttosto discutibile, ma penso di aver sbagliato.

Devo salvare il PyThreadState quando si inizializza l'interprete e lo si sostituisce quando lo finisco (non ho idea del motivo per cui ho bisogno di un ThreadState specifico per chiamare Finalize - non dovrebbe funzionare anche ogni stato?)

In ogni modo l'esempio se altre persone hanno ottenuto lo stesso problema:

PyThreadState *mainstate; 

void initPython(){ 
    PyEval_InitThreads(); 
    Py_Initialize(); 
    mainstate = PyThreadState_Swap(NULL); 
    PyEval_ReleaseLock(); 
} 

void exitPython(){ 
    PyEval_AcquireLock(); 
    PyThreadState_Swap(mainstate); 
    Py_Finalize(); 
} 

l'unico problema è, che io possa acquisire il blocco come ogni altro thread, anche se ci sono ancora discussioni di lavoro. L'API non menziona cosa succede quando Finalize() viene chiamato mentre altri thread continuano a funzionare. Sembra l'esempio perfetto di una condizione di gara ..

1

Hai provato commentando fuori tutto il 'lavoro' fatto nei tuoi fili? Sostituirlo con un loop occupato o un sonno o qualcosa del genere. Ciò aiuterà a stabilire se si tratta del codice di inizializzazione/spegnimento o di qualcosa che stai effettivamente facendo in Python. Forse non stai configurando i thread correttamente - ci sono molte funzioni specifiche per i thread nell'API C e non sono sicuro di quali siano necessarie per garantire il corretto funzionamento.

+0

Ho commentato tutto tranne PyGILState_Ensure() e Release() e l'errore si verifica ancora. Se commento anche loro non c'è problema .. – Voo

+0

In quel caso, immagino ci sia qualcosa nella gestione dei thread che non viene eseguita correttamente. Sfortunatamente la pagina dell'API C con tutte le funzioni del thread su di esso è tutt'altro che ovvia su quale di quelle chiamate è necessaria. – Kylotan

+0

Le "chiamate necessarie" la maggior parte del tempo sono "Py_BEGIN_ALLOW_THREADS" e la sua controparte. Quelle sono, a loro volta, macro che usano 'PyEval_SaveThread()' e la sua controparte. Quindi se scrivessi qualcosa come OP, seguirei quell'esempio. – Kevin

1

Anche io sto riscontrando un problema simile durante l'esecuzione di script contenenti pyxhook da diversi thread tramite l'interprete incorporato.

Non c'è alcun problema se uno script viene eseguito alla volta. L'hook viene rilasciato correttamente ma se due o più script sono eseguiti in parallelo, l'hooking non viene interrotto. Anche se i miei script sono stati restituiti correttamente e cancel() da pyxhook sono stati restituiti correttamente, penso che alcuni thread siano in esecuzione correlati a xlib. Questo problema pyxhook ho risolto mantenendo una bandiera globale per guardare se pyxhook è già in esecuzione e non reinizializzare pyxhook da ogni thread.

Ora per quanto riguarda Py_Finalize(), se pyxhook è reinizializzata in ogni discussione:

se io non chiamo PyEval_AcquireLock() e PyThreadState_Swap() prima di chiamare Py_Finalize() termina in Linux, ma non in Win32. In Win32 c'è un problema se non passo attraverso PyEval_AcquireLock() e PyThreadState_Swap().

Per il momento la soluzione temporanea per me è terminare in modo diverso in due sistemi operativi diversi.