Ich würde nicht concurrent.futures
"fortgeschrittener" nennen - es ist eine einfachere Oberfläche, die sehr ähnlich funktioniert, unabhängig davon, ob Sie mehrere Threads oder mehrere Prozesse als zugrunde liegendes Parallelisierungs-Gimmick verwenden.
Wie bei praktisch allen Fällen einer "einfacheren Benutzeroberfläche" sind also die gleichen Kompromisse erforderlich: Sie weist eine flachere Lernkurve auf, zum großen Teil nur, weil so viel weniger zum Lernen verfügbar ist. Da es jedoch weniger Optionen bietet, kann es Sie möglicherweise in einer Weise frustrieren, wie es die umfangreicheren Schnittstellen nicht tun.
Was CPU-gebundene Aufgaben betrifft, ist dies viel zu wenig spezifiziert, um viel aussagekräftiges zu sagen. Für CPU-gebundene Aufgaben unter CPython benötigen Sie mehrere Prozesse anstelle mehrerer Threads, um eine Beschleunigung zu erzielen. Wie viel (wenn überhaupt) Sie beschleunigen, hängt jedoch von den Details Ihrer Hardware, Ihres Betriebssystems und insbesondere davon ab, wie viel prozessübergreifende Kommunikation Ihre spezifischen Aufgaben erfordern. Unter dem Deckmantel basieren alle prozessübergreifenden Parallelisierungs-Gimmicks auf denselben Betriebssystemprimitiven - die API auf hoher Ebene, die Sie verwenden, um diese zu erhalten, ist kein primärer Faktor für die Geschwindigkeit unter dem Strich.
Bearbeiten: Beispiel
Hier ist der endgültige Code, der in dem Artikel gezeigt wird, auf den Sie verwiesen haben, aber ich füge eine Importanweisung hinzu, die erforderlich ist, damit er funktioniert:
from concurrent.futures import ProcessPoolExecutor
def pool_factorizer_map(nums, nprocs):
# Let the executor divide the work among processes by using 'map'.
with ProcessPoolExecutor(max_workers=nprocs) as executor:
return {num:factors for num, factors in
zip(nums,
executor.map(factorize_naive, nums))}
Hier ist genau das Gleiche mit multiprocessing
stattdessen:
import multiprocessing as mp
def mp_factorizer_map(nums, nprocs):
with mp.Pool(nprocs) as pool:
return {num:factors for num, factors in
zip(nums,
pool.map(factorize_naive, nums))}
Beachten Sie, dass die Möglichkeit, multiprocessing.Pool
Objekte als Kontextmanager zu verwenden, in Python 3.3 hinzugefügt wurde.
Mit welchem ist es einfacher zu arbeiten? LOL ;-) Sie sind im Wesentlichen identisch.
Ein Unterschied besteht darin, dass Pool
so viele verschiedene Methoden unterstützt werden, dass Sie möglicherweise erst erkennen, wie einfach es sein kann , wenn Sie die Lernkurve deutlich nach oben geklettert sind.
Auch hier sind all diese unterschiedlichen Wege sowohl eine Stärke als auch eine Schwäche. Sie sind eine Stärke, da die Flexibilität in einigen Situationen erforderlich sein kann. Sie sind eine Schwäche, weil "vorzugsweise nur ein offensichtlicher Weg, dies zu tun". Ein Projekt, an dem ausschließlich (wenn möglich) concurrent.futures
festgehalten wird, wird auf lange Sicht wahrscheinlich einfacher zu warten sein, da es keine unbegründete Neuheit in der Verwendung seiner minimalen API gibt.
ProcessPoolExecutor
tatsächlich mehr Möglichkeiten als hat ,Pool
weilProcessPoolExecutor.submit
RenditenFuture
Instanzen , die Kündigung (erlaubencancel
), überprüfen die Exception ausgelöst wurde (exception
), und das Hinzufügen von dynamisch einen Rückruf nach Abschluss aufgerufen werden (add_done_callback
). Keine dieser Funktionen ist für von zurückgegebeneAsyncResult
Instanzen verfügbarPool.apply_async
. Auf andere WeisePool
gibt es mehr Optionen aufgrund voninitializer
/initargs
,maxtasksperchild
undcontext
inPool.__init__
und mehr Methoden, die von derPool
Instanz verfügbar gemacht werden.Pool
um die Module ging.Pool
ist ein kleiner Teil dessen, wasmultiprocessing
drin ist, und befindet sich so weit unten in den Dokumenten, dass es eine Weile dauert, bis die Leute erkennen, dass es überhaupt existiertmultiprocessing
. Diese spezielle Antwort konzentrierte sich darauf,Pool
dass dies der gesamte Artikel ist, mit dem das OP verknüpft ist, und dasscf
"viel einfacher zu bearbeiten" einfach nicht wahr ist, was in dem Artikel besprochen wurde. Darüber hinaus,cf
istas_completed()
auch sehr praktisch sein.