Storia della domanda:
Il supporto per i thread è apparso in Perl 5.005, ma a causa delle peculiarità della realizzazione del linguaggio è rimasto a lungo sperimentale e ha presentato un gran numero di bug e limitazioni. A partire da Perl 5.8, il modulo threads (e threads::shared) è diventato sufficientemente stabile per compiti seri, ma il modello dei thread di Perl è molto diverso da molti altri linguaggi di programmazione: ogni thread ottiene la propria copia di tutte le variabili e solo le strutture esplicitamente dichiarate tramite threads::shared sono accessibili in modo condiviso.
Problema:
Le variabili "normali" non sono visibili tra i thread a causa della semantica copy-on-write. Tentare di distribuire dati senza threads::shared porta a una desincronizzazione dello stato. Un uso scorretto dei blocchi comporta il rischio di condizioni di gara, deadlock o modifiche incoerenti.
Soluzione:
Per utilizzare insieme le variabili, dichiarare le variabili condivise tramite use threads::shared. Bloccare l'accesso ai dati condivisi con lock, specialmente se più thread leggono/scrivono contemporaneamente. Per gestire il ciclo di vita dei thread utilizzare i metodi join/detach. Per strutture complesse, dichiarare ogni elemento come shared separatamente, poiché solo il "livello superiore" non garantisce la completa sicurezza dei thread.
Esempio di codice:
use threads; use threads::shared; my $counter :shared = 0; my @threads; for (1..10) { push @threads, threads->create(sub { for (1..1000) { lock($counter); ++$counter; } }); } $_->join() for @threads; print "Counter: $counter ";
Caratteristiche chiave:
Fornisce :shared sicurezza dei thread senza ulteriore lock?
No. L'attributo :shared fornisce accesso alla variabile tra i thread, ma le modifiche (ad esempio, ++ o --) non sono atomiche. È necessario un lock per ogni sezione critica.
È possibile condividere una struttura complessa (array di hash) tra i thread con un'unica direttiva :shared?
No. Solo il "livello superiore" dell'array o dell'hash sarà shared. Ogni elemento annidato deve anche essere condiviso, altrimenti le strutture interne non saranno visibili ad altri thread.
Un thread può terminare un altro thread chiamando exit?
No. exit termina l'intero processo, non un singolo thread. L'arresto di un thread viene effettuato tramite exit all'interno del thread o gestito dalla logica join/detach.
Due thread aumentano contemporaneamente $counter :shared senza lock. Il risultato finale è inferiore a quanto previsto (tipico problema di aggiornamento perso).
Vantaggi:
Svantaggi:
Implementazione di lock su ogni modifica della variabile condivisa. Per strutture grandi, lock annidato elemento per elemento.
Vantaggi:
Svantaggi: