terminación de grupo de multiprocesamiento de Python

Estoy trabajando en una granja de renderizado, y necesito que mis clientes puedan iniciar varias instancias de un renderizador, sin bloquear para que el cliente pueda recibir nuevos comandos. Lo tengo funcionando correctamente, sin embargo, estoy teniendo problemas para terminar los procesos creados.

A nivel global, defino mi grupo (para poder acceder a él desde cualquier función):

p = Pool(2) 

Entonces llamo a mi renderizador con apply_async:

 for i in range(totalInstances): p.apply_async(render, (allRenderArgs[i],args[2]), callback=renderFinished) p.close() 

Esa función finaliza, inicia los procesos en segundo plano y espera nuevos comandos. He hecho un comando simple que matará al cliente y detendrá los renders:

 def close(): 'close this client instance' tn.write ("say "+USER+" is leaving the farm\r\n") try: p.terminate() except Exception,e: print str(e) sys.exit() sys.exit() 

No parece dar un error (se imprimiría el error), Python termina, pero los procesos en segundo plano todavía se están ejecutando. ¿Alguien puede recomendar una mejor manera de controlar estos progtwigs lanzados?

Encontré la solución: detener el grupo en un hilo separado, como este:

 def close_pool(): global pool pool.close() pool.terminate() pool.join() def term(*args,**kwargs): sys.stderr.write('\nStopping...') # httpd.shutdown() stophttp = threading.Thread(target=httpd.shutdown) stophttp.start() stoppool=threading.Thread(target=close_pool) stoppool.daemon=True stoppool.start() signal.signal(signal.SIGTERM, term) signal.signal(signal.SIGINT, term) signal.signal(signal.SIGQUIT, term) 

Funciona bien y siempre lo he probado.

Si aún experimenta este problema, podría intentar simular un Pool con procesos demoníacos (asumiendo que está iniciando el pool / procesos desde un proceso no demoníaco). Dudo que esta sea la mejor solución ya que parece que los procesos Pool su Pool deberían estar saliendo, pero esto es todo lo que pude encontrar. No sé qué hace su callback, así que no estoy seguro de dónde ponerlo en mi ejemplo a continuación.

También sugiero que intente crear su Pool en __main__ debido a mi experiencia (y los documentos), con la rareza que ocurre cuando los procesos se generan globalmente. Esto es especialmente cierto si está en Windows: http://docs.python.org/2/library/multiprocessing.html#windows

 from multiprocessing import Process, JoinableQueue # the function for each process in our pool def pool_func(q): while True: allRenderArg, otherArg = q.get() # blocks until the queue has an item try: render(allRenderArg, otherArg) finally: q.task_done() # best practice to go through main for multiprocessing if __name__=='__main__': # create the pool pool_size = 2 pool = [] q = JoinableQueue() for x in range(pool_size): pool.append(Process(target=pool_func, args=(q,))) # start the pool, making it "daemonic" (the pool should exit when this proc exits) for p in pool: p.daemon = True p.start() # submit jobs to the queue for i in range(totalInstances): q.put((allRenderArgs[i], args[2])) # wait for all tasks to complete, then exit q.join() 

Encontré la respuesta a mi propia pregunta. El problema principal era que estaba llamando a una aplicación de terceros en lugar de a una función. Cuando llamo al subproceso [ya sea usando call () o Popen ()] se crea una nueva instancia de python cuyo único propósito es llamar a la nueva aplicación. Sin embargo, cuando Python salga, eliminará esta nueva instancia de python y dejará la aplicación en ejecución.

La solución es hacerlo de la manera más difícil, encontrando el pid del proceso de python que se crea, obteniendo los hijos de ese pid y matándolos. Este código es específico para osx; Existe un código más simple (que no depende de grep) disponible para Linux.

 for process in pool: processId = process.pid print "attempting to terminate "+str(processId) command = " ps -o pid,ppid -ax | grep "+str(processId)+" | cut -f 1 -d \" \" | tail -1" ps_command = Popen(command, shell=True, stdout=PIPE) ps_output = ps_command.stdout.read() retcode = ps_command.wait() assert retcode == 0, "ps command returned %d" % retcode print "child process pid: "+ str(ps_output) os.kill(int(ps_output), signal.SIGTERM) os.kill(int(processId), signal.SIGTERM)