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()
?
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.
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()))
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)