Escribí el siguiente código
from multiprocessing import Pool class A: def __init__(self) -> None: self.a = 1 def incr(self,b): self.a += 1 print(self.a) def execute(self): with Pool(2) as p: p.starmap(self.incr, [[1],[1],[1],[1]]) a = A() a.execute() print(aa)El resultado es 2 2 2 2 1. Quiero entender qué sucede exactamente en este escenario. ¿El grupo crea cuatro copias de sí mismo? Si es así, ¿cómo se hace esta copia?
En este escenario, el archivo se ejecuta linealmente hasta que se llama a.execute() y llega a la llamada a starmap . Lo que hace el multiprocessing es crear dos procesos más. La forma en que esto se hace depende está limitada por el sistema operativo y puede (en algunos casos seleccionarse usando multiprocessing.set_start_method . Las principales diferencias entre los métodos es que con los métodos 'fork' , el nuevo proceso se crea copiando el proceso actual (es más complicado, pero esa es la idea), mientras que en los métodos 'spawn' se inicia un nuevo intérprete de python, se vuelve a ejecutar el archivo y se realiza la llamada. En este último caso, cuando se vuelve a ejecutar el archivo, se es muy importante usar el if __name__ == "__main__": guard.
Como cada proceso tiene su propia copia de a , lo que sucede en un proceso no afecta a las otras copias de a : incluso si todas las copias de tienen un valor self.a de 2 , el original a no cambia. Puede probar los diferentes métodos para iniciar el proceso con este fragmento de código (retrasos agregados para mayor claridad):
from multiprocessing import Pool, set_start_method, get_start_method import os from time import sleep globalflag = "Flag" class A: def __init__(self) -> None: self.a = 1 def incr(self, b): sleep(b/10) print("Process id:", os.getpid(), " incrementing") print("Flag = ", globalflag) self.a += 1 print(self.a) def execute(self): with Pool() as p: p.starmap(self.incr, [[1],[2],[3],[4]]) print("Running module:", __name__) if __name__ == "__main__": method = 'fork'#'spawn' # 'fork' 'forkserver' if get_start_method(allow_none=True) is None: print("Setting start method to ", method) set_start_method(method) else: print('Start method already set: ', get_start_method()) a = A() globalflag = "Flog" a.execute() print(aa) salida con method='fork' :
Running module: __main__ Start method already set: fork Process id: 6687 incrementing Flag = Flog 2 Process id: 6688 incrementing Flag = Flog 2 Process id: 6689 incrementing Flag = Flog 2 Process id: 6690 incrementing Flag = Flog 2 1salida con 'spawn':
Running module: __main__ Setting start method to spawn Running module: __mp_main__ Running module: __mp_main__ Running module: __mp_main__ Running module: __mp_main__ Running module: __mp_main__ Process id: 7096 incrementing Flag = Flag 2 Running module: __mp_main__ Process id: 7097 incrementing Flag = Flag 2 Running module: __mp_main__ Process id: 7098 incrementing Flag = Flag 2 Running module: __mp_main__ Process id: 7100 incrementing Flag = Flag 2 1intente ejecutar este cambio with Pool(2) as p con:
with Pool(1) as p , with Pool(2) as p , with Pool(4) as p , with Pool(8) as p
y echa un vistazo a las diferentes salidas
from multiprocessing import Pool import os print('PID ;',os.getpid()) ## get the current process id or PID class A: def __init__(self) -> None: self.a = 1 def incr(self,b): self.a += 1 print(self.a) print('PID ;',os.getpid()) def execute(self): with Pool(2) as p: p.starmap(self.incr, [[1],[1],[1],[1]]) a = A() a.execute() print(aa) print('PID ;',os.getpid())recordar:
os.getpid() :
Return the current process id.