2012-05-16 13 views
16

Incruste el intérprete de python en una aplicación C multiproceso y estoy un poco confundido con respecto a las API que debo usar para garantizar la seguridad de los hilos.Incrustar python en la aplicación de C multiproceso

De lo que he reunido, al incrustar pitón que está hasta la embedder para cuidar de la cerradura GIL antes de llamar a cualquier otra llamada a la API de Python C. Esto se hace con estas funciones:

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

Pero esto por sí solo no parece ser suficiente. Todavía tengo bloqueos aleatorios ya que no parece proporcionar exclusión mutua para las API de Python.

Después de leer algunos documentos más También he añadido:

PyEval_InitThreads(); 

justo después de la llamada a Py_IsInitialized() pero ahí es donde la parte confusa viene. Los documentos indican que esta función:

inicializar y adquieren bloquear el intérprete mundial

Esto sugiere que cuando se devuelve esta función, el GIL se supone que es bloqueado y debe ser desbloqueado de alguna manera. pero en la práctica esto no parece ser necesario. Con esta línea, mi multiproceso funcionó perfectamente y la exclusión mutua se mantuvo gracias a las funciones PyGILState_Ensure/Release.
Cuando intenté agregar PyEval_ReleaseLock() después de PyEval_ReleaseLock() la aplicación se bloqueó rápidamente en una llamada posterior al PyImport_ExecCodeModule().

¿Qué me falta aquí?

Respuesta

4

Finalmente me di cuenta.
Después

PyEval_InitThreads(); 

Es necesario llamar

PyEval_SaveThread(); 

Mientras liberar adecuadamente el GIL para el hilo principal.

+0

Esto es incorrecto y potencialmente dañino: 'PyEval_SaveThread' siempre debe estar junto con' PyEval_RestoreThread'. Como se explica en otra parte (http://stackoverflow.com/a/15471525/1600898), no intente liberar el bloqueo después de inicializarlo; simplemente deja que Python lo libere como parte de su trabajo habitual. – user4815162342

+0

No veo por qué es dañino si pones todas las llamadas a python en un bloque _Block_ _Allow_. Por otro lado, si no llamas a 'PyEval_SaveThread();', tu hilo principal bloqueará el acceso de otros hilos a Python. En otras palabras, 'deadlocks PyGILState_Ensure()'. – khkarens

+0

Esto es lo único que funciona tanto para incrustar Python como para llamar a un módulo de extensión. –

-1

Tener una aplicación de C multiproceso que intente comunicarse desde varios subprocesos a varios subprocesos de Python de una única instancia de CPython me parece arriesgada.

Mientras sólo un hilo C se comunica con Python que no debería tener que preocuparse acerca del bloqueo incluso si la aplicación Python es multi-threading. Si necesita varios subprocesos de python, puede configurar la aplicación de esta manera y hacer que varios subprocesos de C se comuniquen a través de una cola con ese único subproceso en C que los distribuye en múltiples subprocesos de Python.

Una alternativa que podría funcionar para usted es tener múltiples instancias de un CPython para cada hilo C que lo necesita (por supuesto de la comunicación entre programas en Python debe ser a través del programa C).

Otra alternativa podría ser el intérprete de Stackless Python. Eso elimina el GIL, pero no estoy seguro de que se encuentre con otros problemas que lo vinculen a varios hilos. stackless fue un reemplazo directo para mi aplicación C (de un solo hilo).

+1

Has hecho lo posible por no responder la pregunta. No estoy interesado en poner en cola el trabajo con un solo hilo. – shoosh

5

Tuve exactamente el mismo problema y ahora se resuelve utilizando PyEval_SaveThread() inmediatamente después de PyEval_InitThreads(), como sugiere anteriormente.Sin embargo, mi problema real fue que utilicé PyEval_InitThreads() después de PyInitialise() que luego hizo que PyGILState_Ensure() se bloqueara cuando se llama desde diferentes subprocesos nativos posteriores. En resumen, esto es lo que hago ahora:

  1. Hay variable global:

    static int gil_init = 0; 
    
  2. Desde un hilo principal cargar la extensión C nativa e iniciar el intérprete de Python:

    Py_Initialize() 
    
  3. Desde varios otros subprocesos, mi aplicación realiza simultáneamente muchas llamadas a la API de Python/C:

    if (!gil_init) { 
        gil_init = 1; 
        PyEval_InitThreads(); 
        PyEval_SaveThread(); 
    } 
    state = PyGILState_Ensure(); 
    // Call Python/C API functions...  
    PyGILState_Release(state); 
    
  4. desde el hilo principal detener el intérprete de Python

    Py_Finalize() 
    

Todas las demás soluciones que he probado, ya sea causada sigfaults Python azar o punto muerto/bloqueo usando PyGILState_Ensure().

La documentación de Python debería ser más clara al respecto y al menos proporcionar un ejemplo para los casos de incorporación y uso de extensión.

Cuestiones relacionadas