web-dev-qa-db-fra.com

Comment puis-je récupérer la valeur de retour d'une fonction passée à multiprocessing.Process?

Dans l'exemple de code ci-dessous, j'aimerais récupérer la valeur de retour de la fonction worker. Comment puis-je m'y prendre? Où cette valeur est-elle stockée?

Exemple de code:

import multiprocessing

def worker(procnum):
    '''worker function'''
    print str(procnum) + ' represent!'
    return procnum


if __== '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        jobs.append(p)
        p.start()

    for proc in jobs:
        proc.join()
    print jobs

Sortie:

0 represent!
1 represent!
2 represent!
3 represent!
4 represent!
[<Process(Process-1, stopped)>, <Process(Process-2, stopped)>, <Process(Process-3, stopped)>, <Process(Process-4, stopped)>, <Process(Process-5, stopped)>]

Je n'arrive pas à trouver l'attribut approprié dans les objets stockés dans jobs.

Merci d'avance, Blz

106
blz

Utilisez variable partagée pour communiquer. Par exemple, comme ceci:

import multiprocessing

def worker(procnum, return_dict):
    '''worker function'''
    print str(procnum) + ' represent!'
    return_dict[procnum] = procnum


if __== '__main__':
    manager = multiprocessing.Manager()
    return_dict = manager.dict()
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,return_dict))
        jobs.append(p)
        p.start()

    for proc in jobs:
        proc.join()
    print return_dict.values()
102
vartec

Je pense que l'approche proposée par @sega_sai est la meilleure. Mais il faut vraiment un exemple de code, alors voici:

import multiprocessing
from os import getpid

def worker(procnum):
    print 'I am number %d in process %d' % (procnum, getpid())
    return getpid()

if __== '__main__':
    pool = multiprocessing.Pool(processes = 3)
    print pool.map(worker, range(5))

Ce qui imprimera les valeurs de retour:

I am number 0 in process 19139
I am number 1 in process 19138
I am number 2 in process 19140
I am number 3 in process 19139
I am number 4 in process 19140
[19139, 19138, 19140, 19139, 19140]

Si vous connaissez map (le Python 2 intégré), cela ne devrait pas être trop difficile. Sinon, regardez lien de sega_Sai .

Notez combien de code est nécessaire. (Notez également comment les processus sont réutilisés).

43
Mark

Cet exemple montre comment utiliser une liste de multiprocessing.Pipe instances pour renvoyer des chaînes à partir d'un nombre arbitraire de processus:

import multiprocessing

def worker(procnum, send_end):
    '''worker function'''
    result = str(procnum) + ' represent!'
    print result
    send_end.send(result)

def main():
    jobs = []
    pipe_list = []
    for i in range(5):
        recv_end, send_end = multiprocessing.Pipe(False)
        p = multiprocessing.Process(target=worker, args=(i, send_end))
        jobs.append(p)
        pipe_list.append(recv_end)
        p.start()

    for proc in jobs:
        proc.join()
    result_list = [x.recv() for x in pipe_list]
    print result_list

if __== '__main__':
    main()

Sortie:

0 represent!
1 represent!
2 represent!
3 represent!
4 represent!
['0 represent!', '1 represent!', '2 represent!', '3 represent!', '4 represent!']

Cette solution utilise moins de ressources qu'un multiprocessing.Queue qui utilise

  • un tuyau
  • au moins une serrure
  • un tampon
  • un fil

ou un multiprocessing.SimpleQueue qui utilise

  • un tuyau
  • au moins une serrure

Il est très instructif d’examiner la source de chacun de ces types.

14
David Cullen

Il semble que vous devriez plutôt utiliser la classe multiprocessing.Pool et les méthodes .apply () .apply_async (), map ()

http://docs.python.org/library/multiprocessing.html?highlight=pool#multiprocessing.pool.AsyncResult

9
sega_sai

Pour une raison quelconque, je ne trouvais pas d'exemple général expliquant comment faire cela avec Queue n'importe où (même les exemples de documentation Python ne génèrent pas plusieurs processus), voici donc ce que j'ai obtenu après 10 essais:

def add_helper(queue, arg1, arg2): # the func called in child processes
    ret = arg1 + arg2
    queue.put(ret)

def multi_add(): # spawns child processes
    q = Queue()
    processes = []
    rets = []
    for _ in range(0, 100):
        p = Process(target=add_helper, args=(q, 1, 2))
        processes.append(p)
        p.start()
    for p in processes:
        ret = q.get() # will block
        rets.append(ret)
    for p in processes:
        p.join()
    return rets

Queue est une file d'attente bloquée et thread-safe que vous pouvez utiliser pour stocker les valeurs de retour des processus enfants. Vous devez donc passer la file d'attente à chaque processus. Ce qui est moins évident ici, c'est que vous devez get() de la file d'attente avant de join la Processes ou bien la file d'attente se remplit et bloque tout.

Mise à jour pour ceux qui sont orientés objet (testé en Python 3.4):

from multiprocessing import Process, Queue

class Multiprocessor():

    def __init__(self):
        self.processes = []
        self.queue = Queue()

    @staticmethod
    def _wrapper(func, queue, args, kwargs):
        ret = func(*args, **kwargs)
        queue.put(ret)

    def run(self, func, *args, **kwargs):
        args2 = [func, self.queue, args, kwargs]
        p = Process(target=self._wrapper, args=args2)
        self.processes.append(p)
        p.start()

    def wait(self):
        rets = []
        for p in self.processes:
            ret = self.queue.get()
            rets.append(ret)
        for p in self.processes:
            p.join()
        return rets

# tester
if __== "__main__":
    mp = Multiprocessor()
    num_proc = 64
    for _ in range(num_proc): # queue up multiple tasks running `sum`
        mp.run(sum, [1, 2, 3, 4, 5])
    ret = mp.wait() # get all results
    print(ret)
    assert len(ret) == num_proc and all(r == 15 for r in ret)
9
sudo

Vous pouvez utiliser la variable exit intégrée pour définir le code de sortie d'un processus. Il peut être obtenu à partir de l'attribut exitcode du processus:

import multiprocessing

def worker(procnum):
    print str(procnum) + ' represent!'
    exit(procnum)

if __== '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        jobs.append(p)
        p.start()

    result = []
    for proc in jobs:
        proc.join()
        result.append(proc.exitcode)
    print result

Sortie:

0 represent!
1 represent!
2 represent!
3 represent!
4 represent!
[0, 1, 2, 3, 4]
9
David Cullen

Pour quiconque cherche à obtenir une valeur de Process en utilisant Queue:

import multiprocessing

ret = {'foo': False}

def worker(queue):
    ret = queue.get()
    ret['foo'] = True
    queue.put(ret)

if __== '__main__':
    queue = multiprocessing.Queue()
    queue.put(ret)
    p = multiprocessing.Process(target=worker, args=(queue,))
    p.start()
    print queue.get()  # Prints {"foo": True}
    p.join()
7
Matthew Moisen

Le paquet pebble a une abstraction de Nice tirant parti de multiprocessing.Pipe, ce qui rend cela assez simple:

from pebble import concurrent

@concurrent.process
def function(arg, kwarg=0):
    return arg + kwarg

future = function(1, kwarg=1)

print(future.result())

Exemple tiré de: https://pythonhosted.org/Pebble/#concurrent-decorators

1
erikreed

Si vous utilisez Python 3, vous pouvez utiliser concurrent.futures.ProcessPoolExecutor comme une abstraction pratique:

_from concurrent.futures import ProcessPoolExecutor

def worker(procnum):
    '''worker function'''
    print(str(procnum) + ' represent!')
    return procnum


if __== '__main__':
    with ProcessPoolExecutor() as executor:
        print(list(executor.map(worker, range(5))))
_

Sortie:

_0 represent!
1 represent!
2 represent!
3 represent!
4 represent!
[0, 1, 2, 3, 4]
_
0
Aleph Aleph

Une solution simple:

import multiprocessing

output=[]
data = range(0,10)

def f(x):
    return x**2

def handler():
    p = multiprocessing.Pool(64)
    r=p.map(f, data)
    return r

if __== '__main__':
    output.append(handler())

print(output[0])

Sortie:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
0
Rubens_Z

J'ai légèrement modifié la réponse de vartec car je devais obtenir les codes d'erreur de la fonction. (Merci vertec !!! c'est un truc génial)

Cela peut aussi être fait avec un manager.list mais je pense qu’il est préférable de l’avoir dans un dict et d’y stocker une liste. De cette façon, nous conservons la fonction et les résultats, car nous ne pouvons pas être sûrs de l'ordre dans lequel la liste sera générée.

from multiprocessing import Process
import time
import datetime
import multiprocessing


def func1(fn, m_list):
    print 'func1: starting'
    time.sleep(1)
    m_list[fn] = "this is the first function"
    print 'func1: finishing'
    # return "func1"  # no need for return since Multiprocess doesnt return it =(

def func2(fn, m_list):
    print 'func2: starting'
    time.sleep(3)
    m_list[fn] = "this is function 2"
    print 'func2: finishing'
    # return "func2"

def func3(fn, m_list):
    print 'func3: starting'
    time.sleep(9)
    # if fail wont join the rest because it never populate the dict
    # or do a try/except to get something in return.
    raise ValueError("failed here")
    # if we want to get the error in the manager dict we can catch the error
    try:
        raise ValueError("failed here")
        m_list[fn] = "this is third"
    except:
        m_list[fn] = "this is third and it fail horrible"
        # print 'func3: finishing'
        # return "func3"


def runInParallel(*fns):  # * is to accept any input in list
    start_time = datetime.datetime.now()
    proc = []
    manager = multiprocessing.Manager()
    m_list = manager.dict()
    for fn in fns:
        # print fn
        # print dir(fn)
        p = Process(target=fn, name=fn.func_name, args=(fn, m_list))
        p.start()
        proc.append(p)
    for p in proc:
        p.join()  # 5 is the time out

    print datetime.datetime.now() - start_time
    return m_list, proc

if __== '__main__':
    manager, proc = runInParallel(func1, func2, func3)
    # print dir(proc[0])
    # print proc[0]._name
    # print proc[0].name
    # print proc[0].exitcode

    # here you can check what did fail
    for i in proc:
        print i.name, i.exitcode  # name was set up in the Process line 53

    # here will only show the function that worked and where able to populate the 
    # manager dict
    for i, j in manager.items():
        print dir(i)  # things you can do to the function
        print i, j
0
pelos