Capitolo 13: Concorrenza & Parallelismo
Impara a scrivere codice concorrente e parallelo in Python usando thread, processi, futures e asyncio.
Scaricachapter13.py
Obiettivi
- Usare
threading.Thread
eLock
per thread semplici. - Sfruttare
multiprocessing.Process
e pool per compiti CPU-bound. - Utilizzare
concurrent.futures
per un'interfaccia unificata. - Scrivere coroutine
asyncio
conasync
/await
. - Comprendere il GIL di Python e quando usare thread vs processi.
1. threading
Avvia thread leggeri:
import threading
def worker(n):
print(f"Thread {n} avviato")
import time; time.sleep(0.5)
print(f"Thread {n} terminato")
threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
Proteggi dati condivisi con un Lock
:
lock = threading.Lock()
counter = 0
def safe_increment():
global counter
with lock:
temp = counter
temp += 1
counter = temp
2. multiprocessing
Esegui codice in processi separati:
from multiprocessing import Process, Pool
def worker(x):
print(f"Processo {x}")
def f(x):
return x*x
if __name__ == "__main__":
procs = [Process(target=worker, args=(i,)) for i in range(2)]
for p in procs:
p.start()
for p in procs:
p.join()
with Pool(4) as pool:
print(pool.map(f, [1,2,3,4]))
Multiprocessing aggira il GIL per compiti CPU-bound.
3. concurrent.futures
API high-level unificata:
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def square(x): return x*x
with ThreadPoolExecutor(max_workers=3) as executor:
print("Threads:", list(executor.map(square, range(5))))
with ProcessPoolExecutor(max_workers=3) as executor:
print("Processi:", list(executor.map(square, range(5))))
4. asyncio
Scrivi coroutine asincrone:
import asyncio
async def say_after(delay, msg):
await asyncio.sleep(delay)
print(msg)
async def main():
task1 = asyncio.create_task(say_after(1, "hello"))
task2 = asyncio.create_task(say_after(2, "world"))
print("Avviate le task")
await task1
await task2
asyncio.run(main())
Usa asyncio.gather()
per eseguire coroutine in parallelo.
Esercizi
- Scrivi un contatore con thread che incrementa in sicurezza una variabile condivisa 10.000 volte.
- Calcola i fattoriali da 1 a 10 in parallelo usando un pool di processi.
- Usa
ThreadPoolExecutor
per recuperare URL concorrenti (usarequests
). - Scrivi un server e client TCP echo con
asyncio
.