2012-05-16 7 views
16

Sto incorporando l'interprete Python in un'applicazione C multithread e sono un po 'confuso su quali API dovrei usare per garantire la sicurezza dei thread.Embedded python nell'applicazione C multithread

Da ciò che ho raccolto, quando si incorpora python, spetta all'embeditore di occuparsi del blocco GIL prima di chiamare qualsiasi altra chiamata API Python C. Questo è fatto con queste funzioni:

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

Ma questo da solo non sembra essere sufficiente. Ho ancora arresti anomali casuali poiché non sembra fornire l'esclusione reciproca per le API Python.

Dopo aver letto alcune ulteriori documenti ho anche aggiunto:

PyEval_InitThreads(); 

subito dopo la chiamata a Py_IsInitialized() ma è lì che la parte confusa viene. I documenti affermano che questa funzione:

inizializzazione e acquisiscono bloccare l'interprete globale

Questo suggerisce che quando questa funzione restituisce, la GIL dovrebbe essere bloccato e deve essere sbloccato in qualche modo. ma in pratica questo non sembra essere richiesto. Con questa linea sul posto il mio multithread ha funzionato perfettamente e l'esclusione reciproca è stata mantenuta dalle funzioni PyGILState_Ensure/Release.
Quando ho provato ad aggiungere dopo l'PyEval_ReleaseLock() l'app bloccata in modo piuttosto rapido in una successiva chiamata a PyImport_ExecCodeModule().

Quindi cosa mi manca qui?

risposta

4

Alla fine ho capito.
Dopo

PyEval_InitThreads(); 

È necessario chiamare

PyEval_SaveThread(); 

Mentre correttamente rilasciare il GIL per il thread principale.

+0

Questo è sbagliato e potenzialmente dannoso: 'PyEval_SaveThread' dovrebbe sempre essere in congiunzione con' PyEval_RestoreThread'. Come [spiegato altrove] (http://stackoverflow.com/a/15471525/1600898), non provare a rilasciare il blocco dopo averlo inizializzato; lasciatelo a Python per rilasciarlo come parte del suo normale lavoro. – user4815162342

+0

Non vedo perché è dannoso se metti tutte le chiamate a python in blocchi _Block_ _Allow_. D'altra parte, se non chiami "PyEval_SaveThread();" allora il tuo thread principale bloccherà l'accesso di altri thread a Python. In altre parole 'PyGILState_Ensure()' deadlock. – khkarens

+0

Questa è l'unica cosa che funziona sia per incorporare Python che per chiamare in un modulo di estensione. –

-1

Avere un'app C multi-thread che tenta di comunicare da più thread a più thread Python di una singola istanza CPython mi sembra rischioso.

Fintanto che solo un thread C comunica con Python, non ci si deve preoccupare del blocco anche se l'applicazione Python è multi-threading. Se sono necessari più thread Python, è possibile impostare l'applicazione in questo modo e far comunicare più thread C tramite una coda con quel singolo thread C che li esegue in batch su più thread Python.

Un'alternativa che potrebbe funzionare per voi è avere più istanze CPython una per ogni thread C che ne ha bisogno (ovviamente la comunicazione tra i programmi Python dovrebbe avvenire tramite il programma C).

Un'altra alternativa potrebbe essere l'interprete Python senza pila. Questo elimina il GIL, ma non sono sicuro che tu incontri altri problemi legandoli a più thread. stackless era una sostituzione drop-in per la mia applicazione C (single-threaded).

+1

Hai fatto del tuo meglio non rispondendo effettivamente alla domanda. Non sono interessato ad accodare il lavoro a un singolo thread. – shoosh

5

Ho avuto esattamente lo stesso problema e ora viene risolto utilizzando PyEval_SaveThread() immediatamente dopo PyEval_InitThreads(), come suggerito sopra.Tuttavia, il mio problema reale era che ho usato PyEval_InitThreads() dopo PyInitialise() che ha causato il blocco di PyGILState_Ensure() quando chiamato da diversi thread nativi successivi. In sintesi, questo è quello che faccio ora:

  1. C'è variabile globale:

    static int gil_init = 0; 
    
  2. da un thread principale caricare l'estensione nativa C e avviare l'interprete Python:

    Py_Initialize() 
    
  3. Da più thread la mia app crea contemporaneamente molte chiamate nell'API Python/C:

    if (!gil_init) { 
        gil_init = 1; 
        PyEval_InitThreads(); 
        PyEval_SaveThread(); 
    } 
    state = PyGILState_Ensure(); 
    // Call Python/C API functions...  
    PyGILState_Release(state); 
    
  4. dal thread principale fermare l'interprete Python

    Py_Finalize() 
    

Tutte le altre soluzioni che ho provato sia causato sigfaults Python casuali o situazione di stallo/blocco utilizzando PyGILState_Ensure().

La documentazione di Python dovrebbe essere più chiara su questo e almeno fornire un esempio per i casi di utilizzo di incorporamento e di estensione.