Je travaille sur une ferme de rendu, et j'ai besoin que mes clients puissent lancer plusieurs instances d'un moteur de rendu, sans bloquer pour que le client puisse recevoir de nouvelles commandes. J'ai que cela fonctionne correctement, mais j'ai du mal à terminer les processus créés.
Au niveau global, je définis mon pool (pour pouvoir y accéder depuis n'importe quelle fonction):
p = Pool(2)
J'appelle ensuite mon rendu avec apply_async:
for i in range(totalInstances):
p.apply_async(render, (allRenderArgs[i],args[2]), callback=renderFinished)
p.close()
Cette fonction se termine, lance les processus en arrière-plan et attend de nouvelles commandes. J'ai fait une commande simple qui tuera le client et arrêtera les rendus:
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()
Il ne semble pas donner d'erreur (il afficherait l'erreur), le python se termine mais les processus d'arrière-plan sont toujours en cours. Quelqu'un peut-il recommander une meilleure façon de contrôler ces programmes lancés?
J'ai trouvé une solution: arrêter le pool dans un thread séparé, comme ceci:
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)
Fonctionne bien et j'ai toujours testé.
Si vous rencontrez toujours ce problème, vous pouvez essayer de simuler un Pool
avec processus démoniaques (en supposant que vous démarrez le pool/processus à partir d'un processus non démoniaque). Je doute que ce soit la meilleure solution car il semble que vos processus Pool
devraient se terminer, mais c'est tout ce que j'ai pu trouver. Je ne sais pas ce que fait votre rappel, donc je ne sais pas où le mettre dans mon exemple ci-dessous.
Je suggère également d'essayer de créer votre Pool
dans __main__
en raison de mon expérience (et des documents) avec l'étrangeté se produisant lorsque les processus sont générés à l'échelle mondiale. Cela est particulièrement vrai si vous êtes sous 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()
# -*- coding:utf-8 -*-
import multiprocessing
import time
import sys
import threading
from functools import partial
#> work func
def f(a,b,c,d,e):
print('start')
time.sleep(4)
print(a,b,c,d,e)
###########> subProcess func
#1. start a thead for work func
#2. waiting thead with a timeout
#3. exit the subProcess
###########
def mulPro(f, *args, **kwargs):
timeout = kwargs.get('timeout',None)
#1.
t = threading.Thread(target=f, args=args)
t.setDaemon(True)
t.start()
#2.
t.join(timeout)
#3.
sys.exit()
if __name__ == "__main__":
p = multiprocessing.Pool(5)
for i in range(5):
#1. process the work func with "subProcess func"
new_f = partial(mulPro, f, timeout=8)
#2. fire on
p.apply_async(new_f, args=(1,2,3,4,5),)
# p.apply_async(f, args=(1,2,3,4,5), timeout=2)
for i in range(10):
time.sleep(1)
print(i+1,"s")
p.close()
# p.join()