concurrent.futures-Modul ThreadPoolExecutor, ProcessPoolExecutor Erklärung und Anwendungsbeispiele

Importieren Sie concurrent.futures.ThreadPoolExecutor

import concurrent.futures

Detaillierte Erläuterung des concurrent.futures-Moduls

Dieses Modul ist eine Standardbibliothek für die gleichzeitige Ausführung von Python und verfügt über Funktionen wie Thread-Pool und Prozesspool, Verwaltung paralleler Programmieraufgaben, Handhabung nicht deterministischer Ausführungsprozesse und Prozess-/Thread-Synchronisation.

  • Modulzusammensetzung
    1. concurrent.futures.Executor: Dies ist eine virtuelle Basisklasse, die Methoden für die asynchrone Ausführung bereitstellt.
    2. Submit(Funktion, Argument): Planen Sie die Ausführung der Funktion (aufrufbares Objekt) und übergeben Sie das Argument als Parameter.
    3. Map(Funktion, Argument): Führen Sie die Funktion mit dem Argument als Parameter asynchron aus.
    4. Shutdown(Wait=True): Sendet ein Signal an den Executor, alle Ressourcen freizugeben.
    5. concurrent.futures.Future: Dazu gehört die asynchrone Ausführung von Funktionen. Zukünftige Objekte sind Instanzen von Übermittlungsaufgaben (d. h. Funktionen mit Parametern) an Ausführende.

Executor ist eine abstrakte Klasse (übergeordnete Klasse), auf die Unterklassen zugreifen können, nämlich ExecutorPools von Threads oder Prozessen. ThreadPoolExecutor ist eine Unterklasse von Executor, die einen Thread-Pool verwendet, um Aufrufe asynchron auszuführen. Da es sich bei Instanzen von Threads oder Prozessen um ressourcenabhängige Aufgaben handelt, ist es am besten, sie in Form eines „Pools“ als wiederverwendbaren Launcher oder Executor zusammenzuorganisieren.

Funktionsanalyse des Quellcodes

drin

class ThreadPoolExecutor(_base.Executor):

    # Used to assign unique thread names when thread_name_prefix is not supplied.
    _counter = itertools.count().__next__

    def __init__(self, max_workers=None, thread_name_prefix='',
                 initializer=None, initargs=()):
        # max_workers参数为空时,默认为机器处理器个数+4,最大值为32
        # thread_name_prefix  线程可选名称前缀
        # initializer  初始化工作线程使,指定的可调用函数
        # initargs  传给可调用函数的参数元组
        """Initializes a new ThreadPoolExecutor instance.

        Args:
            max_workers: The maximum number of threads that can be used to
                execute the given calls.
            thread_name_prefix: An optional name prefix to give our threads.
            initializer: A callable used to initialize worker threads.
            initargs: A tuple of arguments to pass to the initializer.
        """
        if max_workers is None:
            # ThreadPoolExecutor is often used to:
            # * CPU bound task which releases GIL
            # * I/O bound task (which releases GIL, of course)
            #
            # We use cpu_count + 4 for both types of tasks.
            # But we limit it to 32 to avoid consuming surprisingly large resource
            # on many core machine.
            max_workers = min(32, (os.cpu_count() or 1) + 4)
        if max_workers <= 0:
            raise ValueError("max_workers must be greater than 0")

        if initializer is not None and not callable(initializer):
            raise TypeError("initializer must be a callable")

        self._max_workers = max_workers
        self._work_queue = queue.SimpleQueue()
        self._idle_semaphore = threading.Semaphore(0)
        self._threads = set()
        self._broken = False
        self._shutdown = False
        self._shutdown_lock = threading.Lock()
        self._thread_name_prefix = (thread_name_prefix or
                                    ("ThreadPoolExecutor-%d" % self._counter()))
        self._initializer = initializer
        self._initargs = initargs
  • Beispiel
# demo1
import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {
    
    executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

# demo2
import os
import random
import threading
import requests as rq
import time

from threading import Thread, Lock
from queue import Queue  # 用于多线程之间线程安全的数据通信
from concurrent.futures import ThreadPoolExecutor, as_completed

pool = ThreadPoolExecutor()
'''
利用线程池对I/O密集型任务进行优化
with ThreadPoolExecutor() as pool:
        futures = [pool.submit(craw, url) for url in urls]
        for future in futures:#as_completed后的结果顺序是不固定的
            print(future.result())
            html_queue.put(future.result())'''

def event_1():
    print("event_1 started")
    time.sleep(1)
    print("event_1 ended")
    return 1


def event_2():
    print("event_2 started")
    time.sleep(2)
    print("event_2 ended")
    return 2


def event_3():
    print("event_3 started")
    time.sleep(3)
    print("event_3 ended")
    return 3


def main():
    t0 = time.time()
    res1 = pool.submit(event_1)
    res2 = pool.submit(event_2)
    res3 = pool.submit(event_3)
    print(res1.result())
    print(res2.result())
    print(res3.result())
    t1 = time.time()
    print(t1 - t0)


if __name__ == '__main__':
    main()


einreichen

„submit(*args, **kwargs)“
sorgt dafür, dass der aufrufbare Fn als fn(*args, **kwargs) ausgeführt wird, und gibt ein Future-Objekt zurück, das seine Ausführung darstellt.

 with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(pow, 323, 1235)
    print(future.result())

Karte

  • map(func, *iterables, timeout=None, chunksize=1)

  • Ähnlich wie die integrierte Funktionszuordnung (func, *iterables), es gibt jedoch zwei Unterschiede:
    1. Iterables sofort statt verzögerter Erfassung abrufen;
    2. func asynchron ausführen und mehrere gleichzeitige Aufrufe unterstützen.
    Es gibt einen Iterator zurück.

  • Wenn nach einer Zeitüberschreitung von Sekunden seit dem Aufruf von Executor.map() next () auf dem Iterator aufgerufen wird und keine Ergebnisse verfügbar sind, löst der Iterator eine concurrent.futures.TimeoutError-Ausnahme aus.

  • Timeout-Sekunden können eine Gleitkommazahl oder eine Ganzzahl sein. Wenn „Keine“ festgelegt oder nicht angegeben wird, gibt es keine Begrenzung für die Wartezeit.

  • Wenn der Aufruf von func eine Ausnahme auslöst, wird diese Ausnahme ausgelöst, wenn der Wert vom Iterator abgerufen wird.

  • Bei Verwendung von ProcessPoolExecutor unterteilt diese Methode iterierbare Dateien in Blöcke und übermittelt sie als unabhängige Aufgaben an den Prozesspool. Die ungefähre Größe dieser Blöcke kann durch Angabe einer positiven Ganzzahl für „chunksize“ angegeben werden. Bei sehr langen Iterables kann die Verwendung einer größeren Chunksize anstelle des Standardwerts 1 die Leistung erheblich verbessern. Für ThreadPoolExecutor hat chunksize keine Auswirkung.

Unabhängig von der Ausführungsreihenfolge gleichzeitiger Aufgaben gibt die Karte immer Werte basierend auf der Eingabereihenfolge zurück. Der von Map zurückgegebene Iterator wartet auf die Antwort jedes Elements, wenn das Hauptprogramm iteriert.

    def xiaotu_receive_thread(self, *phone):
        for i in phone:
            res = self.number_parameter(i)
            userid, nickname, token, sign = res
            print(res)
            s = f"{
      
      token['S']}"
            t = f"{
      
      token['T']}"
            c_list = (s, t, userid)
        cooke_list = [c_list] * 10
        with concurrent.futures.ThreadPoolExecutor() as pool:
            pool.map(AccountNumber().xiaotu_receive, cooke_list)

Shutdown(wait=True)

  • Teilt dem Executor mit, dass alle vom Executor verwendeten Ressourcen freigegeben werden sollen, nachdem die Ausführung aller derzeit wartenden zukünftigen Objekte abgeschlossen ist.
  • Wenn Sie Executor.submit() und Executor.map() nach dem Herunterfahren aufrufen, wird RuntimeError gemeldet.
  • Wenn „wait“ den Wert „True“ hat, kehrt diese Methode erst zurück, wenn alle erwarteten Futures ausgeführt wurden und die Ressourcen des Executors freigegeben wurden.
  • Wenn „wait“ den Wert „False“ hat, kehrt diese Methode sofort zurück. Ressourcen des Executors werden freigegeben, nachdem alle ausstehenden Futures ausgeführt wurden.
  • Unabhängig vom Wert von „wait“ wird das gesamte Python-Programm nicht beendet, bis die Ausführung des erwarteten Futures abgeschlossen ist.
  • Sie können den expliziten Aufruf dieser Methode vermeiden, indem Sie die with-Anweisung verwenden. Die with-Anweisung ruft die Methode Executor.shutdown() mit dem Standardargument wait=True auf.

Die Executor-Klasse Executor implementiert das Kontextprotokoll und kann als Kontextmanager verwendet werden. Es führt Aufgaben gleichzeitig aus und wartet darauf, dass sie alle abgeschlossen werden. Die Methode „shutdown()“ wird automatisch aufgerufen, wenn der Kontextmanager beendet wird.

import shutil
with ThreadPoolExecutor(max_workers=4) as e:
    e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
    e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
    e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
    e.submit(shutil.copy, 'src4.txt', 'dest4.txt')

ProcessPoolExecutor Prozesspool-Executor

  • ProcessPoolExecutor verwendet das Multiprocessing-Modul, das es ihm ermöglicht, die globale Interpretersperre zu umgehen, aber auch bedeutet, dass er nur auswählbare Objekte ausführen und zurückgeben kann.

  • Das Hauptmodul muss vom Worker-Unterprozess importiert werden, was bedeutet, dass ProcessPoolExecutor in einem interaktiven Interpreter nicht funktioniert.

  • Die Verwendung von Executor- oder Future-Methoden innerhalb eines aufrufbaren Objekts, das an ProcessPoolExecutor übermittelt wurde, führt zu einem Deadlock.

  • concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None, initializer=None, initargs=())
    1. Diese Executor-Unterklasse verwendet höchstens max_workers-Prozesse, um Aufrufe asynchron auszuführen.
    2. Wenn max_workers nicht angegeben ist oder None ist, wird standardmäßig die Anzahl der Prozessoren der Maschine verwendet.
    3. Wenn max_workers kleiner oder gleich 0 ist, wird eine ValueError-Ausnahme ausgelöst.
    4. mp_context ist ein Multiprocessing-Kontext (Multiprocessing-Kontext) oder None, der zum Starten von Workern verwendet wird.
    5. Wenn mp_context nicht angegeben ist oder None ist, wird der Standard-Mehrprozesskontext verwendet.

  • Initializer ist ein optionaler Aufruf, der vor dem Start jedes Arbeitsprozesses aufgerufen wird.

  • initargs ist ein Tupel von Argumenten, die an den Initialisierer übergeben werden.

  • Wenn der Initialisierer eine Ausnahme auslöst, lösen alle derzeit wartenden Aufgaben eine BrokenProcessPool-Ausnahme aus, und wenn die Übermittlungsaufgabe weiterhin gesendet wird, wird diese Ausnahme ebenfalls ausgelöst.

ProcessPoolExecutor-Nutzungsinstanz

import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __name__ == '__main__':
    main()

Guess you like

Origin blog.csdn.net/weixin_43587784/article/details/129167145