web-dev-qa-db-fra.com

python Pool avec les processus de travail

J'essaie d'utiliser un pool de travailleurs dans python en utilisant des objets Process. Chaque travailleur (un processus) effectue une initialisation (prend un temps non négligeable), passe une série de travaux ( idéalement en utilisant map()), et renvoie quelque chose. Aucune communication n'est nécessaire au-delà de cela. Cependant, je n'arrive pas à comprendre comment utiliser map () pour utiliser la fonction compute() de mon employé .

from multiprocessing import Pool, Process

class Worker(Process):
    def __init__(self):
        print 'Worker started'
        # do some initialization here
        super(Worker, self).__init__()

    def compute(self, data):
        print 'Computing things!'
        return data * data

if __name__ == '__main__':
    # This works fine
    worker = Worker()
    print worker.compute(3)

    # workers get initialized fine
    pool = Pool(processes = 4,
                initializer = Worker)
    data = range(10)
    # How to use my worker pool?
    result = pool.map(compute, data)

Est-ce qu'une file d'attente de travaux est la voie à suivre à la place, ou puis-je utiliser map()?

29
Felix

Je vous suggère d'utiliser une file d'attente pour cela.

class Worker(Process):
    def __init__(self, queue):
        super(Worker, self).__init__()
        self.queue = queue

    def run(self):
        print('Worker started')
        # do some initialization here

        print('Computing things!')
        for data in iter(self.queue.get, None):
            # Use data

Vous pouvez maintenant commencer une pile de ces derniers, tout en obtenant du travail à partir d'une seule file d'attente

request_queue = Queue()
for i in range(4):
    Worker(request_queue).start()
for data in the_real_source:
    request_queue.put(data)
# Sentinel objects to allow clean shutdown: 1 per worker.
for i in range(4):
    request_queue.put(None) 

Ce genre de chose devrait vous permettre d'amortir le coût de démarrage coûteux sur plusieurs travailleurs.

51
S.Lott

initializer attend un appelable arbitraire qui effectue l'initialisation, par exemple, il peut définir certains globaux, pas une sous-classe Process; map accepte un itérable arbitraire:

#!/usr/bin/env python
import multiprocessing as mp

def init(val):
    print('do some initialization here')

def compute(data):
    print('Computing things!')
    return data * data

def produce_data():
    yield -100
    for i in range(10):
        yield i
    yield 100

if __name__=="__main__":
  p = mp.Pool(initializer=init, initargs=('arg',))
  print(p.map(compute, produce_data()))
6
jfs

Depuis python 3.3 vous pouvez utiliser starmap, aussi pour utiliser plusieurs arguments ET récupérer les résultats dans une syntaxe très simpliste:

import multiprocessing

nb_cores = multiprocessing.cpu_count()

def caps(nb, letter):
    print('Exec nb:', nb)
    return letter.upper()

if __name__ == '__main__':

    multiprocessing.freeze_support() # for Windows, also requires to be in the statement: if __name__ == '__main__'

    input_data = ['a','b','c','d','e','f','g','h']
    input_order = [1,2,3,4,5,6,7,8,9]

    with multiprocessing.Pool(processes=nb_cores) as pool: # auto closing workers
        results = pool.starmap(caps, Zip(input_order, input_data))

    print(results)
1
Charly Empereur-mot