web-dev-qa-db-fra.com

multitraitement python - le processus se bloque lors d'une jointure pour une file d'attente volumineuse

J'utilise Python 2.7.3 et j'ai remarqué le comportement étrange suivant. Considérez cet exemple minimal:

from multiprocessing import Process, Queue

def foo(qin, qout):
    while True:
        bar = qin.get()
        if bar is None:
            break
        qout.put({'bar': bar})

if __== '__main__':
    import sys

    qin = Queue()
    qout = Queue()
    worker = Process(target=foo,args=(qin,qout))
    worker.start()

    for i in range(100000):
        print i
        sys.stdout.flush()
        qin.put(i**2)

    qin.put(None)
    worker.join()

Lorsque je boucle plus de 10 000 ou plus, mon script se bloque sur worker.join(). Cela fonctionne bien lorsque la boucle ne dépasse pas 1 000.

Des idées?

20
user545424

La file d'attente qout du sous-processus est saturée. Les données que vous avez saisies à partir de foo() n'entrent pas dans la mémoire tampon des canaux du système d'exploitation utilisés en interne. Le sous-processus bloque ainsi l'ajustement d'une plus grande quantité de données. Mais le processus parent ne lit pas ces données: elles sont simplement bloquées aussi, en attendant la fin du sous-processus. C'est une impasse typique.

28
Armin Rigo

Il doit y avoir une limite sur la taille des files d'attente. Considérez la modification suivante:

from multiprocessing import Process, Queue

def foo(qin,qout):
    while True:
        bar = qin.get()
        if bar is None:
            break
        #qout.put({'bar':bar})

if __name__=='__main__':
    import sys

    qin=Queue()
    qout=Queue()   ## POSITION 1
    for i in range(100):
        #qout=Queue()   ## POSITION 2
        worker=Process(target=foo,args=(qin,))
        worker.start()
        for j in range(1000):
            x=i*100+j
            print x
            sys.stdout.flush()
            qin.put(x**2)

        qin.put(None)
        worker.join()

    print 'Done!'

Cela fonctionne tel quel (avec la ligne qout.put mise en commentaire). Si vous essayez de sauvegarder tous les 100 000 résultats, alors la variable qout devient trop grande: si je décommente la fonction qout.put({'bar':bar}) dans foo et que la définition de qout reste dans POSITION 1, le code se bloque. Si, toutefois, je déplace la définition qout vers POSITION 2, le script se termine.

En bref, vous devez faire attention à ce que ni qin ni qout ne deviennent trop grands. (Voir aussi: La taille maximale de la file d'attente de multitraitement est de 32767 )

3
amd

J'ai eu le même problème sur python3 lorsque j'ai essayé de mettre des chaînes dans une file d'attente d'une taille totale d'environ 5 000 cahrs.

Dans mon projet, il y avait un processus hôte qui établit une file d'attente et démarre un sous-processus, puis se joint. Afrer join Le processus hôte lit la file d'attente. Lorsque le sous-processus produit trop de données, l'hôte se bloque sur join. J'ai résolu ce problème en utilisant la fonction suivante pour attendre un sous-processus dans le processus hôte:

def yield_from_process(q, p):
    while p.is_alive():
        p.join(timeout=1)
        while True:
            try:
                yield q.get(block=False)
            except Empty:
                break

Je lis de la file dès qu'il se remplit pour qu'il ne devienne jamais très grand

3
Rugnar

J'essayais de .get() un travailleur asynchrone après la fermeture du pool

erreur d'indentation en dehors d'un bloc with

j'ai eu ceci

with multiprocessing.Pool() as pool:
    async_results = list()
    for job in jobs:
        async_results.append(
            pool.apply_async(
                _worker_func,
                (job,),
            )
        )
# wrong
for async_result in async_results:
    yield async_result.get()

j'avais besoin de ça

with multiprocessing.Pool() as pool:
    async_results = list()
    for job in jobs:
        async_results.append(
            pool.apply_async(
                _worker_func,
                (job,),
            )
        )
    # right
    for async_result in async_results:
        yield async_result.get()
0
ThorSummoner