it-swarm.com.de

Threading-Pool ähnlich dem Multiprocessing-Pool?

Gibt es eine Pool-Klasse für Worker-Threads , ähnlich der Pool-Klasse des Multiprocessing-Moduls?

Ich mag zum Beispiel die einfache Möglichkeit, eine Kartenfunktion zu parallelisieren

def long_running_func(p):
    c_func_no_gil(p)

p = multiprocessing.Pool(4)
xs = p.map(long_running_func, range(100))

ich würde es jedoch gerne tun, ohne den Aufwand, neue Prozesse zu erstellen.

Ich weiß über die GIL Bescheid. In meinem Anwendungsfall ist die Funktion jedoch eine E/A-gebundene C-Funktion, für die der python wrapper die GIL vor dem eigentlichen Funktionsaufruf freigibt.

Muss ich meinen eigenen Threading-Pool schreiben?

309
Martin

Ich habe gerade herausgefunden, dass es tatsächlich is eine Thread-basierte Pool-Schnittstelle im multiprocessing -Modul gibt, die jedoch etwas versteckt und nicht richtig dokumentiert ist.

Es kann über importiert werden

from multiprocessing.pool import ThreadPool

Es wird mit einer Dummy-Prozessklasse implementiert, die einen python thread umschließt. Diese threadbasierte Prozessklasse befindet sich in multiprocessing.dummy was im docs kurz erwähnt wird. Dieses Dummy-Modul bietet angeblich die gesamte auf Threads basierende Multiprocessing-Schnittstelle.

413
Martin

In Python 3 können Sie concurrent.futures.ThreadPoolExecutor , d. h .:

executor = ThreadPoolExecutor(max_workers=10)
a = executor.submit(my_function)

Weitere Informationen und Beispiele finden Sie in docs .

192
Adrian Adamiak

Ja, und es scheint (mehr oder weniger) die gleiche API zu haben.

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)
....
55
warfares

Für etwas sehr Einfaches und Leichtes (leicht modifiziert von hier ):

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

Um Rückrufe nach Abschluss der Aufgabe zu unterstützen, können Sie den Rückruf einfach zum Aufgabentupel hinzufügen.

37
dgorissen

Hallo, um den Thread-Pool in Python zu verwenden, können Sie diese Bibliothek verwenden:

from multiprocessing.dummy import Pool as ThreadPool

und dann für den Gebrauch, diese Bibliothek tun so:

pool = ThreadPool(threads)
results = pool.map(service, tasks)
pool.close()
pool.join()
return results

Bei den Threads handelt es sich um die Anzahl der gewünschten Threads, und bei den Aufgaben handelt es sich um eine Liste der Aufgaben, die dem Dienst am häufigsten zugeordnet sind.

7

Hier ist das Ergebnis, das ich letztendlich verwendet habe. Es ist eine modifizierte Version der Klassen von dgorissen oben.

Datei: 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)

Den Pool benutzen

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()
5
forumulator

Der Aufwand für das Erstellen der neuen Prozesse ist minimal, insbesondere wenn es sich nur um vier Prozesse handelt. Ich bezweifle, dass dies ein Leistungsschwerpunkt Ihrer Anwendung ist. Halten Sie es einfach, optimieren Sie, wo Sie müssen und wohin die Ergebnisse der Profilerstellung führen.

2
unbeli

Es gibt keinen eingebauten Thread-basierten Pool. Es kann jedoch sehr schnell gehen, eine Producer/Consumer-Warteschlange mit der Klasse Queue zu implementieren.

Von: 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
1
Yann Ramin