Entiendo que hay una variedad de técnicas para compartir memoria y estructuras de datos entre procesos en python. Esta pregunta es específicamente sobre esta memoria inherentemente compartida en los scripts de python que existían en python 3.6 pero parece que ya no existen en 3.10. ¿Alguien sabe por qué y si es posible recuperar esto en 3.10? ¿O qué es este cambio que estoy observando? Actualicé mi Mac a Monterey y ya no es compatible con Python 3.6, por lo que me veo obligado a actualizar a 3.9 o 3.10+.
Nota: Tiendo a desarrollar en Mac y ejecutar la producción en Ubuntu. No estoy seguro si eso influye aquí. Históricamente, con 3.6, todo se comportaba igual independientemente del sistema operativo.
Haga un proyecto simple con los siguientes archivos de python
miBiblioteca.py
MyDict = {}prueba.py
import threading import time import multiprocessing import myLibrary def InitMyDict(): myLibrary.MyDict = {'woot': 1, 'sauce': 2} print('initialized myLibrary.MyDict to ', myLibrary.MyDict) def MainLoop(): numOfSubProcessesToStart = 3 for i in range(numOfSubProcessesToStart): t = threading.Thread( target=CoolFeature(), args=()) t.start() while True: time.sleep(1) def CoolFeature(): MyProcess = multiprocessing.Process( target=SubProcessFunction, args=()) MyProcess.start() def SubProcessFunction(): print('SubProcessFunction: ', myLibrary.MyDict) if __name__ == '__main__': InitMyDict() MainLoop()Cuando ejecuto esto en 3.6, tiene un comportamiento significativamente diferente al de 3.10. Entiendo que un subproceso no puede modificar la memoria del proceso principal, pero aun así es muy conveniente acceder a la estructura de datos del proceso principal que se configuró previamente en lugar de mover cada pequeña cosa a la memoria compartida solo para leer un simple diccionario/int/cadena/etc.
Salida de Python 3.10:
python3.10 test.py initialized myLibrary.MyDict to {'woot': 1, 'sauce': 2} SubProcessFunction: {} SubProcessFunction: {} SubProcessFunction: {}Salida de Python 3.6:
python3.6 test.py initialized myLibrary.MyDict to {'woot': 1, 'sauce': 2} SubProcessFunction: {'woot': 1, 'sauce': 2} SubProcessFunction: {'woot': 1, 'sauce': 2} SubProcessFunction: {'woot': 1, 'sauce': 2}Observación:
Observe que en 3.6, el subproceso puede ver el valor que se estableció desde el proceso principal. Pero en 3.10, el subproceso ve un diccionario vacío.
En resumen, desde 3.8, CPython usa el método de inicio de generación en MacO . Antes usaba el método del tenedor .
En las plataformas UNIX, se utiliza el método de inicio de bifurcación , lo que significa que cada nuevo proceso de multiprocessing es una copia exacta del padre en el momento de la bifurcación.
El método de generación significa que inicia un nuevo intérprete de Python para cada nuevo proceso de multiprocessing . Según la documentación:
El proceso secundario solo heredará los recursos necesarios para ejecutar el método
run()del objeto de proceso.
Importará su programa a este nuevo intérprete, por lo que iniciar procesos, etcétera, solo se debe realizar desde el if __name__ == '__main__': -block!
Esto significa que no puede contar con que las variables del proceso padre estén disponibles en los hijos, a menos que sean constantes de nivel de módulo que se importarían .
Así que el cambio es significativo.
¿Qué se puede hacer?
Si la información requerida pudiera ser una constante a nivel de módulo, eso resolvería el problema de la manera más simple.
Si eso no es posible (por ejemplo, porque los datos deben generarse en tiempo de ejecución), puede hacer que el padre escriba la información para compartirla en un archivo. Por ejemplo, en formato JSON y antes de que inicie otros procesos. Entonces los niños podrían simplemente leer esto. Esa es probablemente la siguiente solución más simple.
El uso de un administrador de multiprocessing.Manager le permitiría compartir un dict entre procesos. Sin embargo, hay una cierta cantidad de gastos generales asociados con esto.
O puede intentar llamar a multiprocessing.set_start_method("fork") antes de crear procesos o grupos y ver si no falla en su caso. Eso volvería al método anterior a 3.8 en MacOs. Pero como se documenta en este error , existen problemas reales con el uso del método de fork en MacO. Leer el problema indica que la fork podría estar bien siempre que no use hilos .