Capitolo 13: Concorrenza & Parallelismo

Impara a scrivere codice concorrente e parallelo in Python usando thread, processi, futures e asyncio.

Scarica chapter13.py

Obiettivi

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

  1. Scrivi un contatore con thread che incrementa in sicurezza una variabile condivisa 10.000 volte.
  2. Calcola i fattoriali da 1 a 10 in parallelo usando un pool di processi.
  3. Usa ThreadPoolExecutor per recuperare URL concorrenti (usa requests).
  4. Scrivi un server e client TCP echo con asyncio.