threading - python threads vs processes
Python multi-thread multi-interpreter API de Python (1)
Los intérpretes de sub en Python no están bien documentados o incluso bien soportados. Lo siguiente es lo mejor de mi comprensión. Parece funcionar bien en la práctica.
Hay dos conceptos importantes que se deben entender al tratar con subprocesos y subprocesos en Python. En primer lugar, el intérprete de Python no es realmente multiproceso. Tiene un bloqueo de intérprete global (GIL) que debe adquirirse para realizar casi cualquier operación de Python (hay algunas excepciones raras a esta regla).
En segundo lugar, cada combinación de subprocesos y subprocesos debe tener su propio estado de subprocesos. El intérprete crea un estado de subproceso para cada subproceso administrado por él, pero si desea utilizar Python desde un subproceso no creado por ese intérprete, debe crear un nuevo estado de subproceso.
Primero necesitas crear los sub interpretes:
Inicializar Python
Py_Initialize();
Inicializar el soporte de hilos de Python
Requerido si planea llamar a Python desde múltiples hilos). Esta convocatoria también adquiere la GIL.
PyEval_InitThreads();
Guardar el estado del hilo actual
Podría haber usado PyEval_SaveThread()
, pero uno de sus efectos secundarios es liberar la GIL, que luego debe volver a adquirirse.
PyThreadState* _main = PyThreadState_Get();
Crear los sub intérpretes
PyThreadState* ts1 = Py_NewInterpreter();
PyThreadState* ts2 = Py_NewInterpreter();
Restaurar el estado del hilo principal del intérprete
PyThreadState_Swap(_main);
Ahora tenemos dos estados de subprocesos para los sub interpretes. Estos estados de hilo solo son válidos en el hilo donde se crearon. Cada subproceso que quiera usar uno de los subprocesadores debe crear un estado de subproceso para esa combinación de subproceso e intérprete.
Usando un sub interprete de un nuevo hilo
Aquí hay un código de ejemplo para usar un subprocturador en un subproceso nuevo que no fue creado por el intérprete sub. El nuevo subproceso debe adquirir el GIL, crear un nuevo estado de subproceso para la combinación de subproceso e interpretación y convertirlo en el estado de subproceso actual. Al final se debe hacer lo contrario para limpiar.
void do_stuff_in_thread(PyInterpreterState* interp)
{
// acquire the GIL
PyEval_AcquireLock();
// create a new thread state for the the sub interpreter interp
PyThreadState* ts = PyThreadState_New(interp);
// make ts the current thread state
PyThreadState_Swap(ts);
// at this point:
// 1. You have the GIL
// 2. You have the right thread state - a new thread state (this thread was not created by python) in the context of interp
// PYTHON WORK HERE
// release ts
PyThreadState_Swap(NULL);
// clear and delete ts
PyThreadState_Clear(ts);
PyThreadState_Delete(ts);
// release the GIL
PyEval_ReleaseLock();
}
Ahora cada hilo puede hacer lo siguiente:
Hilo1
do_stuff_in_thread(ts1->interp);
Hilo 2
do_stuff_in_thread(ts1->interp);
Hilo3
do_stuff_in_thread(ts2->interp);
Al llamar a Py_Finalize()
destruyen todos los Py_Finalize()
. Alternativamente, el puede ser destruido manualmente. Esto debe hacerse en el subproceso principal, utilizando los estados de subproceso creados al crear los intérpretes sub. Al final, haga que el hilo principal del intérprete establezca el estado actual.
// make ts1 the current thread state
PyThreadState_Swap(ts1);
// destroy the interpreter
Py_EndInterpreter(ts1);
// make ts2 the current thread state
PyThreadState_Swap(ts2);
// destroy the interpreter
Py_EndInterpreter(ts2);
// restore the main interpreter thread state
PyThreadState_Swap(_main);
Espero que esto haga las cosas un poco más claras.
Tengo un pequeño ejemplo completo escrito en C ++ en github .
Estoy jugando con la API de C para Python, pero es bastante difícil entender algunos casos de esquina. Podría probarlo, pero parece que es propenso a errores y consume mucho tiempo. Así que vengo aquí para ver si alguien ya ha hecho esto.
La pregunta es, ¿cuál es la forma correcta de administrar un subproceso con subprocesos múltiples, sin relación directa entre los subprocesos y subprocesos?
Py_Initialize();
PyEval_InitThreads(); /* <-- needed? */
_main = PyEval_SaveThread(); /* <-- acquire lock? does it matter? */
/* maybe do I not need it? */
i1 = Py_NewInterpreter();
i2 = Py_NewInterpreter();
¿Uso un mutex? ¿Se requiere usar cerraduras? La función de subprocesos debe ser algo como lo siguiente: (Los subprocesos son no de python, probablemente subprocesos de POSIX)
Hilo1
_save = PyThreadState_Swap(i1);
// python work
PyThreadState_Restore(_save);
Hilo 2 (casi idéntico)
_save = PyThreadState_Swap(i1);
// python work
PyThreadState_Restore(_save);
Thread3 (casi idéntico, pero con el sub-intérprete i2
)
_save = PyThreadState_Swap(i2);
// python work
PyThreadState_Restore(_save);
¿Es esto correcto? ¿Es este el caso general que quiero lograr? ¿Hay condiciones de carrera?
¡Gracias!