ProgrammazioneSviluppatore Python

Cosa succede quando si passano oggetti compositi (ad esempio, liste di dizionari) tra thread o processi in Python? Cosa è importante ricordare quando si programmano applicazioni multithreading e multiprocessing che coinvolgono collezioni mutabili?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

In Python, quando si lavora con thread, gli oggetti in memoria sono accessibili a tutti i thread senza copia. Tuttavia, quando si lavora con processi tramite multiprocessing, gli oggetti standard di Python (ad esempio, liste, dizionari) vengono trasferiti tra i processi tramite serializzazione (di solito tramite pickle), e ogni processo riceve la propria copia dell'oggetto. Le modifiche all'oggetto in un processo non si riflettono sulla copia in un altro processo.

Differenze chiave:

  • Multithreading (threading): Tutti i thread operano nello stesso spazio di indirizzamento. Qualsiasi oggetto (inclusi collezioni annidate) è condiviso.
  • Multiprocessing (multiprocessing): Ogni processo ha la propria memoria; gli oggetti vengono trasferiti tramite serializzazione. Per la comunicazione tra processi devono essere utilizzate strutture speciali, ad esempio, multiprocessing.Manager().dict().

Esempio:

# Multiprocessing from multiprocessing import Process, Manager def worker(d): d['a'] = 42 if __name__ == '__main__': # Un dict normale non funziona tra processi managed_dict = Manager().dict() p = Process(target=worker, args=(managed_dict,)) p.start(); p.join() print(managed_dict['a']) # 42
  • Se fosse stato utilizzato un dict normale, le modifiche nel processo non sarebbero state visibili al processo principale.

Domanda insidiosa.

In quali casi la modifica di una lista normale (list) in un processo si rifletterà in un altro processo?

Risposta:

Mai, se si utilizza multiprocessing. Gli oggetti Python normali non sono condivisi tra i processi. Per la condivisione è necessario utilizzare primitive speciali—oggetti Manager (ad esempio, Manager().list(), Manager().dict()), che sincronizzano lo stato tra i processi.

Esempi di errori reali a causa della mancanza di conoscenza delle sottigliezze dell'argomento.


Storia

Il team di sviluppatori di un crawler web ha inizializzato le code di lavori come liste e le ha condivise tra processi tramite multiprocessing. I lavori "scomparivano" perché i processi non vedevano gli aggiornamenti reciproci. Hanno corretto usando Manager().list().


Storia

In un servizio analitico, i registri tentavano di essere raccolti in dizionari annidati all'interno dei thread. In uno scenario ad alta richiesta questo ha portato a condizioni di competizione e danneggiamento dei dati a causa della mancanza di lock.


Storia

In un servizio ETL, quando hanno tentato di raccogliere oggetti di dati raccolti durante la fase di elaborazione in molti processi, hanno scoperto duplicazione/perdita di dati: gli sviluppatori non avevano considerato che ogni processo lavorava con la propria copia della struttura, e non con una comune.