Non definirei concurrent.futures
più "avanzato": è un'interfaccia più semplice che funziona allo stesso modo indipendentemente dal fatto che si utilizzino più thread o più processi come espediente di parallelizzazione sottostante.
Così, come quasi tutte le istanze di "un'interfaccia più semplice", più o meno le stesse trade-off sono coinvolti: ha una curva di apprendimento meno profonda, in gran parte solo perché c'è così tanto meno disponibili ad imparare; ma, poiché offre un minor numero di opzioni, alla fine potrebbe frustrarti come non faranno le interfacce più ricche.
Per quanto riguarda le attività legate alla CPU, è troppo poco specificato per dire molto significativo. Per le attività associate alla CPU in CPython, sono necessari più processi anziché più thread per avere la possibilità di ottenere uno speedup. Ma la quantità (se presente) di una velocità che ottieni dipende dai dettagli del tuo hardware, del tuo sistema operativo e in particolare dalla quantità di comunicazione tra processi richiesta dalle tue attività specifiche. Sotto le copertine, tutti i trucchi di parallelizzazione tra processi si basano sulle stesse primitive del sistema operativo: l'API di alto livello che usi per ottenere a quelli non è un fattore primario nella velocità di fondo.
Modifica: esempio
Ecco il codice finale mostrato nell'articolo a cui hai fatto riferimento, ma sto aggiungendo una dichiarazione di importazione necessaria per farlo funzionare:
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))}
Ecco esattamente la stessa cosa usando multiprocessing
invece:
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))}
Si noti che la possibilità di utilizzare gli multiprocessing.Pool
oggetti come gestori di contesto è stata aggiunta in Python 3.3.
Con quale è più facile lavorare? LOL ;-) Sono sostanzialmente identici.
Una differenza è che Pool
supporta così tanti modi diversi di fare le cose che potresti non renderti conto di quanto possa essere facile fino a quando non hai scalato abbastanza in alto la curva di apprendimento.
Ancora una volta, tutti questi modi diversi sono sia una forza che una debolezza. Sono un punto di forza perché la flessibilità può essere richiesta in alcune situazioni. Sono una debolezza a causa di "preferibilmente solo un modo ovvio per farlo". Un progetto basato esclusivamente (se possibile) su concurrent.futures
sarà probabilmente più facile da mantenere a lungo termine, a causa della mancanza di novità gratuite nel modo in cui può essere utilizzata la sua API minima.