web-dev-qa-db-fra.com

Comment obtenir les résultats d'un pool de threads en python?

J'ai cherché ici comment faire du filetage en python, mais de loin je n'ai pas pu obtenir la réponse dont j'ai besoin. Je ne connais pas très bien les classes Queue et Threading python et pour cette raison, certaines des réponses présentées ici n'ont aucun sens pour moi.

Je veux créer un pool de threads que je peux assigner à différentes tâches et quand tous seront terminés, obtenir les valeurs de résultat et les traiter. Jusqu'à présent, j'ai essayé de le faire, mais je ne suis pas en mesure d'obtenir les résultats. Le code que j'ai écrit est:

from threading import Thread
from Queue import Queue

class Worker(Thread):
    """Thread executing tasks from a given tasks queue"""
    def __init__(self, tasks):
        Thread.__init__(self)
        self.tasks = tasks
        self.daemon = True
        self.result = None
        self.start()
    def run(self):
        while True:
            func, args, kargs = self.tasks.get()
            try:
                self.result = func(*args, **kargs)
            except Exception, e:
                print e
            self.tasks.task_done()
    def get_result(self):
        return self.result

class ThreadPool:
    """Pool of threads consuming tasks from a queue"""
    def __init__(self, num_threads):
        self.tasks = Queue(num_threads)
        self.results = []
        for _ in range(num_threads):
            w = Worker(self.tasks)
            self.results.append(w.get_result())
    def add_task(self, func, *args, **kargs):
        """Add a task to the queue"""
        self.tasks.put((func, args, kargs))
    def wait_completion(self):
        """Wait for completion of all the tasks in the queue"""
        self.tasks.join()
    def get_results(self):
        return self.results

def foo(Word, number):
    print Word*number
    return number

words = ['hello', 'world', 'test', 'Word', 'another test']
numbers = [1,2,3,4,5]
pool = ThreadPool(5)
for i in range(0, len(words)):
    pool.add_task(foo, words[i], numbers[i])

pool.wait_completion()
results = pool.get_results()
print results

La sortie imprime les chaînes avec Word donné fois le nombre donné mais la liste des résultats est pleine avec des valeurs None, donc où je devrais mettre les valeurs de retour du func.

Ou le moyen le plus simple est de créer une liste dans laquelle je remplis la file d'attente et j'ajoute un dictionnaire ou une variable pour stocker le résultat en tant qu'argument dans ma fonction, et après l'ajout de la tâche à la file d'attente, ajoutez cet argument de résultat à une liste de résultats :

def foo(Word, number, r):
    print Word*number
    r[(Word,number)] = number
    return number

words = ['hello', 'world', 'test', 'Word', 'another test']
numbers = [1,2,3,4,5]
pool = ThreadPool(5)
results = []
for i in range(0, len(words)):
    r = {}
    pool.add_task(foo, words[i], numbers[i], r)
    results.append(r)
print results
21
Rafael Rios

Python a en fait un pool de threads intégré que vous pouvez utiliser, ce n'est tout simplement pas bien documenté :

from multiprocessing.pool import ThreadPool

def foo(Word, number):
    print (Word * number)
    r[(Word,number)] = number
    return number

words = ['hello', 'world', 'test', 'Word', 'another test']
numbers = [1,2,3,4,5]
pool = ThreadPool(5)
results = []
for i in range(0, len(words)):
    results.append(pool.apply_async(foo, args=(words[i], numbers[i])))

pool.close()
pool.join()
results = [r.get() for r in results]
print results

Ou (en utilisant map au lieu de apply_async):

from multiprocessing.pool import ThreadPool

def foo(Word, number):
    print Word*number
    return number

def starfoo(args):
    """ 

    We need this because map only supports calling functions with one arg. 
    We need to pass two args, so we use this little wrapper function to
    expand a zipped list of all our arguments.

    """    
    return foo(*args)

words = ['hello', 'world', 'test', 'Word', 'another test']
numbers = [1,2,3,4,5]
pool = ThreadPool(5)
# We need to Zip together the two lists because map only supports calling functions
# with one argument. In Python 3.3+, you can use starmap instead.
results = pool.map(starfoo, Zip(words, numbers))
print results

pool.close()
pool.join()
30
dano