1. Multiprocessing (attività legate alla CPU):
- Quando usare: Ideale per compiti che sono intensivi computazionalmente (legati alla CPU), come scricchiolio dei numeri, elaborazione delle immagini o calcoli complessi. Questi compiti beneficiano di più dall'utilizzo di più core.
- come funziona: Crea processi separati, ognuno con il proprio spazio di memoria. Ciò evita le limitazioni del blocco dell'interprete globale (GIL), consentendo una vera esecuzione parallela.
- Esempio:
`` `Python
Import Multiprocessing
tempo di importazione
def processo_item (elemento):
"" "Simula un'attività legata alla CPU." ""
time.sleep (1) # simula lavoro
restituzione elemento * 2
def main ():
elementi =elenco (intervallo (10))
start_time =time.time ()
con multiprocessing.pool (processi =multiprocessing.cpu_count ()) come pool:
Risultati =pool.map (process_item, articoli)
end_time =time.time ()
print (f "Risultati:{risultati}")
print (f "time prese:{end_time - start_time:.2f} secondi")
Se __Name__ =="__main__":
principale()
`` `
- Spiegazione:
- `multiprocessing.pool`:crea un pool di processi di lavoro. `multiprocessing.cpu_count ()` utilizza automaticamente il numero di core disponibili.
- `pool.map`:applica la funzione` processo_item` a ciascun elemento nell'elenco `elementi ', distribuendo il lavoro attraverso i processi del lavoratore. Gestisce automaticamente la suddivisione del lavoro e la raccolta dei risultati.
- `pool.apply_async`:un'alternativa non bloccante a` pool.map`. È necessario raccogliere risultati usando `result.get ()` per ogni articolo.
- `pool.imap` e` pool.imap_unorgerded`:iteratori che restituiscono risultati man mano che diventano disponibili. `IMAP_UNORDERD` non garantisce l'ordine dei risultati.
- `pool.starmap`:Simile a` pool.map`, ma consente di passare più argomenti alla funzione del lavoratore usando le tuple.
- Vantaggi:
- Supera la limitazione GIL per le attività legate alla CPU.
- Utilizza più core della CPU in modo efficiente.
- Svantaggi:
- Overhead più elevato rispetto al threading a causa della creazione di processi separati.
- La comunicazione tra i processi (passaggi di passaggio) può essere più lenta.
- Più intenso memoria, poiché ogni processo ha il proprio spazio di memoria.
- Può essere più complesso per gestire lo stato condiviso (necessita di meccanismi di comunicazione tra processi come code o memoria condivisa).
2. Discussione (attività legate a I/O):
- Quando usare: Adatto a compiti che trascorrono un periodo significativo in attesa di operazioni esterne (I/O-Bound), come richieste di rete, letture/scritture del disco o query di database.
- come funziona: Crea più thread all'interno di un singolo processo. I thread condividono lo stesso spazio di memoria. Il GIL (Interprete Global Interpreter) limita il vero parallelismo in Cpython, ma i thread possono ancora migliorare le prestazioni rilasciando GIL quando aspettano l'I/O.
- Esempio:
`` `Python
threading di importazione
tempo di importazione
def fetch_url (URL):
"" "Simula un'attività legata all'I/O." ""
print (f "Fetching {url}")
time.sleep (2) # simula il ritardo della rete
print (f "Finito Fetching {url}")
return f "contenuto di {url}"
def main ():
urls =["https://example.com/1", "https://example.com/2", "https://example.com/3"]
start_time =time.time ()
threads =[]
Risultati =[]
per URL negli URL:
thread =threading.Thread (target =lambda U:risultati.append (fetch_url (u)), args =(url,))
threads.append (thread)
thread.start ()
per thread in thread:
thread.Join () # Attendere il completamento di tutti i thread
end_time =time.time ()
print (f "Risultati:{risultati}")
print (f "time prese:{end_time - start_time:.2f} secondi")
Se __Name__ =="__main__":
principale()
`` `
- Spiegazione:
- `Threading.Thread`:crea un nuovo thread.
- `thread.start ()`:avvia l'esecuzione del thread.
- `thread.Join ()`:attende che il thread finisca.
- Funzione lambda: Usato per passare l'URL` come argomento a `fetch_url` all'interno del costruttore` thread '. È essenziale passare l'url` * per valore * per evitare le condizioni di gara in cui tutti i thread potrebbero finire per usare l'ultimo valore di `url '.
- Vantaggi:
- Overhead inferiore rispetto al multiprocessing.
- condivide lo spazio di memoria, rendendo più semplice condividere i dati tra i thread (ma richiede un'attenta sincronizzazione).
- Può migliorare le prestazioni per le attività legate all'I/O nonostante il GIL.
- Svantaggi:
- Il GIL limita il vero parallelismo per i compiti legati alla CPU in Cpython.
- Richiede un'attenta sincronizzazione (blocchi, semafori) per prevenire le condizioni di gara e la corruzione dei dati quando si accede alle risorse condivise.
3. Asyncio (concorrenza con un singolo thread):
- Quando usare: Eccellente per gestire un gran numero di compiti legati all'I/O contemporaneamente all'interno di un singolo thread. Fornisce un modo per scrivere un codice asincrono in grado di passare da una attività in attesa di completare le operazioni I/O.
- come funziona: Utilizza un ciclo di eventi per gestire le coroutine (funzioni speciali dichiarate con `async def`). Le coroutine possono sospendere la loro esecuzione in attesa di I/O e consentire l'esecuzione di altre coroutine. `Asyncio` non * fornisce * il vero parallelismo (è concorrenza), ma può essere altamente efficiente per le operazioni legate all'I/O.
- Esempio:
`` `Python
Importa asyncio
tempo di importazione
Async def fetch_url (URL):
"" "Simula un compito legato all'I/O (asincrono)." ""
print (f "Fetching {url}")
Aspetta asyncio.sleep (2) # simula il ritardo di rete (non bloccante)
print (f "Finito Fetching {url}")
return f "contenuto di {url}"
Async def main ():
urls =["https://example.com/1", "https://example.com/2", "https://example.com/3"]
start_time =time.time ()
Attività =[FETCH_URL (URL) per URL negli URL]
Risultati =Aspetta asyncio.Gather (*Attività) # Esegui le attività contemporaneamente
end_time =time.time ()
print (f "Risultati:{risultati}")
print (f "time prese:{end_time - start_time:.2f} secondi")
Se __Name__ =="__main__":
asyncio.run (main ())
`` `
- Spiegazione:
- `Async Def`:definisce una funzione asincrona (coroutine).
- `Abveit`:sospende l'esecuzione della coroutine fino al completamento dell'operazione attesa. Rilascia il controllo del ciclo di eventi, consentendo l'esecuzione di altre coroutine.
- `asyncio.sleep`:una versione asincrona di` time.sleep` che non blocca il ciclo di eventi.
- `Asyncio.Gather`:gestisce contemporaneamente più coroutine e restituisce un elenco dei loro risultati nell'ordine in cui sono stati presentati. `*Attività" disimballa l'elenco delle attività.
- `asyncio.run`:avvia il ciclo di eventi Asyncio e gestisce la coroutine" principale ".
- Vantaggi:
- Altamente efficiente per le attività legate all'I/O, anche con un singolo thread.
- Evita il sovraccarico di creare più processi o thread.
- più facile da gestire la concorrenza rispetto al threading (meno necessità di blocchi espliciti).
- Eccellente per la costruzione di applicazioni di rete altamente scalabili.
- Svantaggi:
- Richiede l'utilizzo di librerie e codice asincroni, che possono essere più complessi da imparare e debug rispetto al codice sincrono.
- Non è adatto per compiti legati alla CPU (non fornisce il vero parallelismo).
- si basa su librerie compatibili con asincroni (ad esempio, `aiohttp` invece di` richieste`).
4. Concomitire.
- Quando usare: Fornisce un'interfaccia di alto livello per l'esecuzione delle attività in modo asincrono, utilizzando thread o processi. Ti consente di passare da threading e multiprocessing senza modificare in modo significativo il codice.
- come funziona: Utilizza `ThreadPoolexecutor` per threading e` ProcessPoolexecutor` per il multiprocessing.
- Esempio:
`` `Python
Importazione Concurrent.Futures
tempo di importazione
def processo_item (elemento):
"" "Simula un'attività legata alla CPU." ""
time.sleep (1) # simula lavoro
restituzione elemento * 2
def main ():
elementi =elenco (intervallo (10))
start_time =time.time ()
con concurrent.Futures.ProcessPoolexecutor (max_workers =multiprocessing.cpu_count ()) come esecutore:
# Invia ogni elemento all'esecutore
Futures =[Executor.Submit (Process_Item, Item) per elemento negli elementi]
# Aspetta che tutti i futuri siano completati e ottieni i risultati
Risultati =[Future.Result () per Future in Conrrent.Futures.as_Completed (Futures)]
end_time =time.time ()
print (f "Risultati:{risultati}")
print (f "time prese:{end_time - start_time:.2f} secondi")
Se __Name__ =="__main__":
Import Multiprocessing
principale()
`` `
- Spiegazione:
- `concurrent.futures.processpoolexecutor`:crea un pool di processi di lavoro. È inoltre possibile utilizzare `concurrent.futures.Threadpoolexecutor` per i thread.
- `Executor.submit`:invia una chiamata (funzione) all'esecutore per l'esecuzione asincrona. Restituisce un oggetto "futuro" che rappresenta il risultato dell'esecuzione.
- `concurrent.futures.as_completed`:un iteratore che produce oggetti" futuri "mentre completano, in nessun ordine particolare.
- `Future.Result ()`:recupera il risultato del calcolo asincrono. Bloccherà fino a quando non sarà disponibile il risultato.
- Vantaggi:
- Interfaccia di alto livello, semplificazione della programmazione asincrona.
- Passa facilmente da thread e processi modificando il tipo di esecutore.
- Fornisce un modo conveniente per gestire compiti asincroni e recuperare i loro risultati.
- Svantaggi:
- Può avere un overhead leggermente più rispetto all'utilizzo diretto di `multiprocessing` o` threading`.
Scegliere l'approccio giusto:
| Approccio | Tipo di attività | GIL Limitazione | Utilizzo della memoria | Complessità |
| ------------------- | ------------------- | ---------------- | ------------------- | ------------ |
| Multiprocessing | CPU-Legato | Superare | Alto | Moderato |
| Discussione | I/O-legato | Sì | Basso | Moderato |
| Asyncio | I/O-legato | Sì | Basso | Alto |
| Concurrent.futures | Entrambi | Dipende | Varia | Basso |
Considerazioni chiave:
* Tipo di attività (legato alla CPU vs. I/O-legato): Questo è il fattore più importante. Le attività legate alla CPU beneficiano del multiprocessing, mentre le attività legate all'I/O sono più adatte per il threading o l'asyncio.
* Gil (Interpreter Lock Global): Il Gil in Cpython limita il vero parallelismo nel threading. Se hai bisogno di un vero parallelismo per le attività legate alla CPU, usa il multiprocessing.
* Overhead: Il multiprocessing ha un sovraccarico più elevato rispetto al threading e all'asyncio.
* Utilizzo della memoria: Il multiprocessing utilizza più memoria perché ogni processo ha il proprio spazio di memoria.
* Complessità: Asyncio può essere più complesso da imparare rispetto al threading o al multiprocessing.
* Condivisione dei dati: La condivisione dei dati tra i processi (multiprocessing) richiede meccanismi di comunicazione tra processi (code, memoria condivisa), che possono aggiungere complessità. I thread condividono lo spazio di memoria, ma richiedono un'attenta sincronizzazione per evitare le condizioni di gara.
* Supporto libreria: Assicurati che le librerie che stai utilizzando siano compatibili con Asyncio se scegli tale approccio. Molte biblioteche ora offrono versioni asincrone (ad esempio, `aiohttp` per le richieste HTTP).
Best practice:
* Profilo del tuo codice: Prima di implementare il parallelismo, profila il codice per identificare i colli di bottiglia. Non ottimizzare prematuramente.
* Misura le prestazioni: Prova approcci diversi e misura le loro prestazioni per determinare quale funziona meglio per il tuo caso d'uso specifico.
* Mantieni le attività indipendenti: Più sono indipendenti i tuoi compiti, più facile sarà parallelizzarli.
* Gestire le eccezioni: Gestire correttamente le eccezioni nelle funzioni o nelle coroutine del lavoratore per impedire loro di arrestare l'intera applicazione.
* Usa le code per la comunicazione: Se è necessario comunicare tra processi o thread, utilizzare le code per evitare le condizioni di gara e garantire la sicurezza dei thread.
* Considera una coda di messaggi: Per sistemi complessi e distribuiti, prendere in considerazione l'utilizzo di una coda di messaggi (ad es. RabbitMQ, Kafka) per l'elaborazione asincrona delle attività.
Considerando attentamente questi fattori, è possibile scegliere l'approccio più efficiente per l'esecuzione del ciclo di corsa Python in parallelo e migliorare significativamente le prestazioni della tua applicazione. Ricorda di testare e misurare i risultati per garantire che l'approccio prescelto stia effettivamente fornendo un beneficio per le prestazioni.
Programmazione © www.354353.com