Multihilo con Python y C api

Tengo un progtwig en C ++ que usa la API de C para usar una biblioteca Python mía. Tanto la biblioteca de Python como el código C ++ son multiproceso.

En particular, un subproceso del progtwig C ++ crea una instancia de un objeto Python que hereda de threading.Thread . Necesito todos mis subprocesos de C ++ para poder llamar a métodos en ese objeto.

Desde mis primeros bashs (ingenuamente solo instalé el objeto desde el hilo principal, luego esperé un tiempo y luego llamé al método) noté que la ejecución del hilo de Python asociado con el objeto que acaba de crearse se detiene tan pronto como vuelve la ejecución. al progtwig C ++.

Si la ejecución se mantiene con Python (por ejemplo, si llamo a PyRun_SimpleString("time.sleep(5)"); ) la ejecución del subproceso de Python continúa en segundo plano y todo funciona bien hasta que la espera finaliza y la ejecución vuelve a C ++ .

Evidentemente estoy haciendo algo mal. ¿Qué debo hacer para que mi C ++ y Python sean multiproceso y sean capaces de trabajar bien entre ellos? No tengo experiencia previa en el campo, ¡así que no asum nada!

Un orden correcto de pasos para realizar lo que está tratando de hacer es:

  • En el hilo principal:

    1. Inicialice Python utilizando Py_Initialize* .
    2. Inicialice el soporte de hilos de Python utilizando PyEval_InitThreads() .
    3. Iniciar el hilo de C ++.

En este punto, el hilo principal aún mantiene la GIL.

  • En un hilo de C ++:
    1. Adquiera la GIL utilizando PyGILState_Ensure() .
    2. Crear un nuevo objeto de hilo de Python e iniciarlo.
    3. Suelte la GIL usando PyGILState_Release() .
    4. Duerme, haz algo útil o sal del hilo.

Debido a que el hilo principal contiene el GIL, este hilo estará esperando para adquirir el GIL. Si el hilo principal llama a la API de Python, puede liberar GIL de vez en cuando permitiendo que el hilo de Python se ejecute por un momento.

  • De vuelta en el hilo principal:
    1. Suelte la GIL, permitiendo que los hilos se ejecuten usando PyEval_SaveThread()
    2. Antes de intentar usar otras llamadas de Python, PyEval_RestoreThread() adquirir la GIL usando PyEval_RestoreThread()

Sospecho que te estás perdiendo el último paso: lanzar GIL en el hilo principal, permitiendo que se ejecute el hilo de Python.

Tengo un pequeño pero completo ejemplo que hace exactamente eso en este enlace .

Es probable que no desbloquee el locking global de intérprete cuando devuelva la llamada desde el threading.Thread de python.

Bueno, si está usando la API C de bare python, aquí tiene algo de documentación sobre cómo liberar / adquirir el GIL. Pero mientras uso C ++, debo advertirte que podría fallar en cualquier excepción al lanzar tu código C ++. Ver aqui

En general, cualquier función de C ++ que se ejecute durante demasiado tiempo debería desbloquear GIL y bloquear, cada vez que use la API de C Python nuevamente.