web-dev-qa-db-fra.com

Python multiprocessing.Queue vs multiprocessing.manager (). Queue ()

J'ai une tâche simple comme ça:

def worker(queue):
    while True:
        try:
            _ = queue.get_nowait()
        except Queue.Empty:
            break

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    # queue = multiprocessing.Queue()
    queue = manager.Queue()

    for i in range(5):
        queue.put(i)

    processes = []

    for i in range(2):
        proc = multiprocessing.Process(target=worker, args=(queue,))
        processes.append(proc)
        proc.start()

    for proc in processes:
        proc.join()

Il semble que le multiprocessing.Queue peut faire tout le travail dont j'avais besoin, mais d'un autre côté, je vois de nombreux exemples de manager (). Queue () et je ne comprends pas ce dont j'ai vraiment besoin. On dirait que Manager (). Queue () utilise une sorte d'objets proxy, mais je ne comprends pas ce but, car multiprocessing.Queue () fait le même travail sans aucun objet proxy.

Donc, mes questions sont:

1) Quelle différence réelle entre multiprocessing.Queue et objet renvoyé par multiprocessing.manager (). Queue ()?

2) Que dois-je utiliser?

31
novicef

Bien que ma compréhension soit limitée à ce sujet, d'après ce que j'ai fait, je peux dire qu'il y a une différence principale entre multiprocessing.Queue () et multiprocessing.Manager (). Queue ():

  • multiprocessing.Queue () est un objet tandis que multiprocessing.Manager (). Queue () est une adresse (proxy) pointant vers la file d'attente partagée gérée par l'objet multiprocessing.Manager ().
  • par conséquent, vous ne pouvez pas passer des objets multiprocessing.Queue () normaux aux méthodes Pool, car ils ne peuvent pas être décapés.
  • De plus, le python doc nous dit de porter une attention particulière lors de l'utilisation du multiprocessing.Queue () car il peut avoir des effets indésirables

Remarque Lorsqu'un objet est placé dans une file d'attente, l'objet est décapé et un thread d'arrière-plan vide ensuite les données décapées dans un canal sous-jacent. Cela a quelques conséquences qui sont un peu surprenantes, mais ne devraient pas poser de problèmes pratiques - si elles vous dérangent vraiment, vous pouvez plutôt utiliser une file d'attente créée avec un gestionnaire. Après avoir placé un objet dans une file d'attente vide, il peut y avoir un délai infinitésimal avant que la méthode empty () de la file d'attente renvoie False et que get_nowait () puisse retourner sans augmenter Queue.Empty. Si plusieurs processus mettent des objets en file d'attente, il est possible que les objets soient reçus à l'autre extrémité dans le désordre. Cependant, les objets mis en file d'attente par le même processus seront toujours dans l'ordre attendu les uns par rapport aux autres.

Avertissement Comme mentionné ci-dessus, si un processus enfant a placé des éléments dans une file d'attente (et qu'il n'a pas utilisé JoinableQueue.cancel_join_thread), ce processus ne se terminera pas jusqu'à ce que tous les articles tamponnés aient été rincés dans le tuyau. Cela signifie que si vous essayez de rejoindre ce processus, vous pouvez obtenir un blocage sauf si vous êtes sûr que tous les éléments qui ont été placés dans la file d'attente ont été consommés. De même, si le processus enfant n'est pas démoniaque, le processus parent peut se bloquer lors de la sortie lorsqu'il essaie de joindre tous ses enfants non démoniaques. Notez qu'une file d'attente créée à l'aide d'un gestionnaire ne présente pas ce problème.

Il existe une solution de contournement pour utiliser le multiprocessing.Queue () avec Pool en définissant la file d'attente en tant que variable globale et en la définissant pour tous les processus lors de l'initialisation:

queue = multiprocessing.Queue()
def initialize_shared(q):
    global queue
    queue=q

pool= Pool(nb_process,initializer=initialize_shared, initargs(queue,))

va créer des processus de pool avec des files d'attente correctement partagées mais nous pouvons affirmer que les objets multiprocessing.Queue () n'ont pas été créés pour cette utilisation.

D'un autre côté, manager.Queue () peut être partagé entre les sous-processus du pool en le passant comme argument normal d'une fonction.

À mon avis, l'utilisation de multiprocessing.Manager (). Queue () est très bien dans tous les cas et moins gênante. Il peut y avoir des inconvénients à utiliser un gestionnaire, mais je n'en suis pas conscient.

25
michael