web-dev-qa-db-fra.com

python pool multiprocessing terminate

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?

12
tk421storm

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é.

7
eri

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()
5
mdscruggs
# -*- 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()
0
rodemon