Existe-t-il une classe de pool pour les threads de travail , similaire au module de multitraitement classe de pool ?
J'aime par exemple le moyen facile de paralléliser une fonction de carte
def long_running_func(p):
c_func_no_gil(p)
p = multiprocessing.Pool(4)
xs = p.map(long_running_func, range(100))
cependant, j'aimerais le faire sans la surcharge liée à la création de nouveaux processus.
Je connais le GIL. Toutefois, dans mon cas d'utilisation, la fonction sera une fonction C liée à l'IO pour laquelle l'encapsuleur python publiera le GIL avant l'appel de la fonction.
Dois-je écrire mon propre pool de threads?
Je viens de découvrir qu'il existe une interface de pool basée sur les threads dans le module multiprocessing
, mais elle est quelque peu masquée et mal documentée. .
Il peut être importé via
_from multiprocessing.pool import ThreadPool
_
Il est implémenté à l'aide d'une classe de processus fictive qui encapsule un thread python. Cette classe de processus basée sur les threads se trouve dans multiprocessing.dummy
, ce qui est brièvement mentionné dans docs . Ce module factice fournit supposément toute l'interface de multitraitement basée sur les threads.
Dans Python 3, vous pouvez utiliser concurrent.futures.ThreadPoolExecutor
, c'est-à-dire:
_executor = ThreadPoolExecutor(max_workers=10)
a = executor.submit(my_function)
_
Voir le docs pour plus d'informations et d'exemples.
Oui, et il semble avoir (plus ou moins) la même API.
import multiprocessing
def worker(lnk):
....
def start_process():
.....
....
if(PROCESS):
pool = multiprocessing.Pool(processes=POOL_SIZE, initializer=start_process)
else:
pool = multiprocessing.pool.ThreadPool(processes=POOL_SIZE,
initializer=start_process)
pool.map(worker, inputs)
....
Pour quelque chose de très simple et léger (légèrement modifié à partir de ici ):
from Queue import Queue
from threading import Thread
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.start()
def run(self):
while True:
func, args, kargs = self.tasks.get()
try:
func(*args, **kargs)
except Exception, e:
print e
finally:
self.tasks.task_done()
class ThreadPool:
"""Pool of threads consuming tasks from a queue"""
def __init__(self, num_threads):
self.tasks = Queue(num_threads)
for _ in range(num_threads):
Worker(self.tasks)
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()
if __== '__main__':
from random import randrange
from time import sleep
delays = [randrange(1, 10) for i in range(100)]
def wait_delay(d):
print 'sleeping for (%d)sec' % d
sleep(d)
pool = ThreadPool(20)
for i, d in enumerate(delays):
pool.add_task(wait_delay, d)
pool.wait_completion()
Pour prendre en charge les rappels à la fin de la tâche, vous pouvez simplement ajouter le rappel à la tâche Tuple.
Bonjour, pour utiliser le pool de threads dans Python, vous pouvez utiliser cette bibliothèque:
from multiprocessing.dummy import Pool as ThreadPool
et pour l’utilisation, cette bibliothèque fait comme ça:
pool = ThreadPool(threads)
results = pool.map(service, tasks)
pool.close()
pool.join()
return results
Les threads sont le nombre de threads que vous souhaitez et les tâches sont une liste des tâches les plus mappées au service.
Voici le résultat que j'ai finalement utilisé. C'est une version modifiée des classes par dgorissen ci-dessus.
Fichier: threadpool.py
from queue import Queue, Empty
import threading
from threading import Thread
class Worker(Thread):
_TIMEOUT = 2
""" Thread executing tasks from a given tasks queue. Thread is signalable,
to exit
"""
def __init__(self, tasks, th_num):
Thread.__init__(self)
self.tasks = tasks
self.daemon, self.th_num = True, th_num
self.done = threading.Event()
self.start()
def run(self):
while not self.done.is_set():
try:
func, args, kwargs = self.tasks.get(block=True,
timeout=self._TIMEOUT)
try:
func(*args, **kwargs)
except Exception as e:
print(e)
finally:
self.tasks.task_done()
except Empty as e:
pass
return
def signal_exit(self):
""" Signal to thread to exit """
self.done.set()
class ThreadPool:
"""Pool of threads consuming tasks from a queue"""
def __init__(self, num_threads, tasks=[]):
self.tasks = Queue(num_threads)
self.workers = []
self.done = False
self._init_workers(num_threads)
for task in tasks:
self.tasks.put(task)
def _init_workers(self, num_threads):
for i in range(num_threads):
self.workers.append(Worker(self.tasks, i))
def add_task(self, func, *args, **kwargs):
"""Add a task to the queue"""
self.tasks.put((func, args, kwargs))
def _close_all_threads(self):
""" Signal all threads to exit and lose the references to them """
for workr in self.workers:
workr.signal_exit()
self.workers = []
def wait_completion(self):
"""Wait for completion of all the tasks in the queue"""
self.tasks.join()
def __del__(self):
self._close_all_threads()
def create_task(func, *args, **kwargs):
return (func, args, kwargs)
Pour utiliser la piscine
from random import randrange
from time import sleep
delays = [randrange(1, 10) for i in range(30)]
def wait_delay(d):
print('sleeping for (%d)sec' % d)
sleep(d)
pool = ThreadPool(20)
for i, d in enumerate(delays):
pool.add_task(wait_delay, d)
pool.wait_completion()
Les frais généraux liés à la création de nouveaux processus sont minimes, en particulier lorsqu'il ne s'agit que de quatre d'entre eux. Je doute que ce soit un point chaud de performance de votre application. Restez simple, optimisez où vous devez et où pointent les résultats du profilage.
Il n'y a pas de pool de threads intégré. Cependant, l'implémentation d'une file d'attente producteur/consommateur avec la classe Queue
peut être très rapide.
De: https://docs.python.org/2/library/queue.html
from threading import Thread
from Queue import Queue
def worker():
while True:
item = q.get()
do_work(item)
q.task_done()
q = Queue()
for i in range(num_worker_threads):
t = Thread(target=worker)
t.daemon = True
t.start()
for item in source():
q.put(item)
q.join() # block until all tasks are done