De hecho, incluso cuando se eliminan todas las referencias del usuario al objeto pool
, y no hay tareas están en el código de la cola, y se realiza toda la recolección de basura, entonces todavía los procesos permanecen zombis como inutilizables en el sistema operativo - además tenemos hilos de servicio 3 de zombies de Pool
colgante (Python 2.7 y 3.4):
>>> del pool
>>> gc.collect()
0
>>> gc.garbage
[]
>>> threading.enumerate()
[<_MainThread(MainThread, started 5632)>, <Thread(Thread-8, started daemon 5252)>,
<Thread(Thread-9, started daemon 5260)>, <Thread(Thread-7, started daemon 7608)>]
y más Pool()
's va a añadir más y más el proceso y el hilo de zombis ... que permanecen hasta que se termine el proceso principal .
Se requiere un empuje especial para detener dicha agrupación zombi - a través de su servicio de hilo _handle_workers
:
>>> ths = threading.enumerate()
>>> for th in ths:
... try: th.name, th._state, th._Thread__target
... except AttributeError: pass
...
('MainThread', 1, None)
('Thread-8', 0, <function _handle_tasks at 0x01462A30>)
('Thread-9', 0, <function _handle_results at 0x014629F0>)
('Thread-7', 0, <function _handle_workers at 0x01462A70>)
>>> ths[-1]._state = multiprocessing.pool.CLOSE # or TERMINATE
>>> threading.enumerate()
[<_MainThread(MainThread, started 5632)>]
>>>
que termina los otros hilos de servicio y también termina los procesos hijos.
creo que uno de los problemas es que hay un error de fugas de recursos en la biblioteca de Python, lo que podría ser fijado por derecho de uso de weakref
's.
El otro punto es que Pool
creación & terminación es caro (incluyendo 3 hilos de servicio por la piscina sólo para la gestión!), Y hay USusually hay razón para tener mucho más procesos de trabajo que los núcleos de CPU (cargas de alta CPU) o más que un número limitado de acuerdo con otro recurso de limitación (por ejemplo, ancho de banda de red). Por lo tanto, es razonable tratar un conjunto más como un recurso global de aplicación singular (administrado opcionalmente por un tiempo de espera) en lugar de un objeto quicky simplemente mantenido por un cierre (o un terminate() - solución debido al error).
Por ejemplo:
try:
_unused = pool # reload safe global var
except NameError:
pool = None
def get_pool():
global pool
if pool is None:
atexit.register(stop_pool)
pool = Pool(CPUCORES)
return pool
def stop_pool():
global pool
if pool:
pool.terminate()
pool = None
le puede dar una pista sobre lo que el '...' 'está dentro de su pool.map (...)'? – SingleNegationElimination
Claro. '...' son operaciones de solo lectura pero con uso intensivo de CPU en las variables miembro del objeto 'Volatile'. Me gustaría que estos se ejecuten en paralelo, para mejorar el rendimiento. El objeto no está mutado durante la duración de 'do_stuff'. – user124114