Tecniche avanzate di sincronizzazione

Aggiornamento: novembre 2007

Le applicazioni multithread si avvalgono spesso di handle di attesa e di oggetti Monitor per la sincronizzazione di più thread. In queste sezioni viene illustrato come utilizzare le seguenti classi .NET Framework durante la sincronizzazione dei thread: AutoResetEvent, Interlocked, ManualResetEvent, Monitor, Mutex, ReaderWriterLock, Timer, WaitHandle.

Handle di attesa

Gli handle di attesa sono oggetti che segnalano lo stato di un thread a un altro thread e possono essere utilizzati dai thread per notificare ad altri thread la necessità di disporre di accesso esclusivo a una risorsa. Gli altri thread saranno quindi obbligati ad attendere e a utilizzare la risorsa solo quando l'handle di attesa non sarà più in uso. Gli handle di attesa dispongono di due stati, segnalato e non segnalato. Un handle di attesa che non appartiene a un thread si trova nello stato segnalato. Un handle di attesa che appartiene a un thread si trova nello stato non segnalato.

I thread richiedono un handle di attesa mediante la chiamata a uno dei metodi di attesa disponibili, ad esempio WaitOne, WaitAny o WaitAll. Tali metodi di attesa sono chiamate bloccanti, simili al metodo Join per un singolo thread:

  • Se l'handle di attesa non è utilizzato da alcun altro thread, la chiamata restituisce immediatamente True, lo stato dell'handle di attesa viene cambiato in non segnalato e il thread che lo utilizza rimane in esecuzione.

  • Se uno dei metodi di attesa di un handle di attesa viene chiamato da un thread, ma l'handle di attesa è utilizzato da un altro thread, il thread chiamante attenderà per un intervallo di tempo specificato (se è stato impostato un timeout) o attenderà senza alcun limite di tempo (se non è stato specificato alcun timeout), fino a quando l'handle di attesa non verrà rilasciato dall'altro thread. Se è stato specificato un timeout e l'handle di attesa viene rilasciato prima dello scadere del timeout, la chiamata restituisce True. In caso contrario, la chiamata restituisce False e il thread chiamante rimane in esecuzione.

Il metodo Set viene chiamato dai thread che utilizzano un handle di attesa una volta completate le operazioni necessarie o quando non necessitano più di tale handle. Altri thread possono reimpostare lo stato di un handle di attesa su non segnalato chiamando il metodo Reset oppure chiamando WaitOne, WaitAny o WaitAll e attendendo che il thread chiami Set. Gli handle AutoResetEvent vengono automaticamente reimpostati su non segnalato dal sistema dopo il rilascio di un singolo thread di attesa. Se nessun thread è in attesa, lo stato dell'oggetto evento resta segnalato.

Esistono tre tipi di handle di attesa comunemente utilizzati con Visual Basic: oggetti mutex, ManualResetEvent e AutoResetEvent. Gli ultimi due handle sono spesso definiti eventi di sincronizzazione.

Oggetti mutex

Gli oggetti mutex sono oggetti di sincronizzazione il cui utilizzo è consentito solo a un singolo thread alla volta. Il nome stesso di tali oggetti deriva dall'inglese "mutually exclusive", che indica l'esclusione reciproca dall'utilizzo di questi oggetti. L'utilizzo dell'oggetto mutex viene richiesto dai thread quando risulta necessario disporre di accesso esclusivo a una risorsa. Poiché un oggetto mutex può essere posseduto solo da un thread in un dato momento, è necessario che gli altri thread attendano il rilascio di tale oggetto prima di utilizzare la risorsa.

Il metodo WaitOne fa in modo che un thread chiamante attenda di poter utilizzare un oggetto mutex. Se un thread termina normalmente durante l'utilizzo di un oggetto mutex, lo stato di tale oggetto viene impostato su segnalato e il successivo thread in attesa viene autorizzato all'utilizzo dell'oggetto mutex.

Eventi di sincronizzazione

Gli eventi di sincronizzazione consentono di comunicare agli altri thread che si è verificato un evento o che una risorsa è disponibile. Nonostante il termine "evento", gli eventi di sincronizzazione non sono come gli altri eventi Visual Basic, ma sono degli handle di attesa. Analogamente agli altri handle di attesa, per gli eventi di sincronizzazione sono previsti due stati: segnalato e non segnalato.

È necessario che i thread che chiamano uno dei metodi di attesa di un evento di sincronizzazione attendano fino alla segnalazione dell'evento da parte di un altro thread mediante la chiamata del metodo Set. Sono disponibili due classi di eventi di sincronizzazione: ManualResetEvent e AutoResetEvent.

Lo stato delle istanze dell'evento ManualResetEvent viene impostato su segnalato dai thread mediante il metodo Set. e lo stato delle istanze dell'evento ManualResetEvent viene impostato su non segnalato mediante il metodo Reset o quando il controllo viene restituito a una chiamata WaitOne.

È possibile impostare su segnalato le istanze della classe AutoResetEvent anche mediante Set ma lo stato tornerà automaticamente su non segnalato nel momento in cui un thread in attesa viene informato che l'evento è segnalato.

Oggetti Monitor e SyncLock

Gli oggetti Monitor vengono utilizzati per assicurare che un blocco di codice venga eseguito senza subire interruzioni da codice in esecuzione su altri thread. In altre parole, il codice presente in altri thread non potrà essere eseguito fino al completamento dell'esecuzione del codice presente nel blocco di codice sincronizzato.

Si supponga ad esempio di disporre di un programma che esegue la lettura ripetuta e asincrona di dati e consente la visualizzazione dei risultati. Nei sistemi operativi che supportano il multitasking di tipo preemptive, un thread in esecuzione può essere interrotto dal sistema operativo per consentire l'esecuzione di altri thread. In assenza di sincronizzazione, è possibile che si ottenga una visualizzazione parzialmente aggiornata dei dati, nel caso in cui l'oggetto che rappresenta i dati venga modificato da un altro thread durante la visualizzazione dei dati. Gli oggetti Monitor vengono utilizzati per assicurare che un blocco di codice venga eseguito senza subire interruzioni. Visual Basic fornisce le istruzioni SyncLock e End SyncLock per semplificare l'accesso agli oggetti Monitor. Visual C# consente si utilizzare la parola chiave Lock nello stesso modo.

Nota:

L'accesso a un oggetto è inibito solo se il codice di accesso è contenuto in un blocco SyncLock sulla stessa istanza dell'oggetto.

Per informazioni sull'istruzione SyncLock, vedere Istruzione SyncLock

Classe Interlocked

I metodi della classe Interlocked consentono di evitare i problemi che potrebbero verificarsi quando il tentativo di aggiornare o confrontare lo stesso valore viene effettuato contemporaneamente da più thread. Tramite i metodi di tale classe è possibile incrementare, decrementare, scambiare e confrontare i valori da qualunque thread. Nell'esempio riportato di seguito viene illustrato come utilizzare il metodo Increment per incrementare una variabile condivisa da routine in esecuzione su thread distinti.

Sub ThreadA(ByRef IntA As Integer)
    System.Threading.Interlocked.Increment(IntA)
End Sub

Sub ThreadB(ByRef IntA As Integer)
    System.Threading.Interlocked.Increment(IntA)
End Sub

Blocchi ReaderWriter

In alcuni casi è possibile che si desideri bloccare una risorsa solamente durante la scrittura di dati e consentire a più client contemporaneamente di leggere i dati quando non sono in fase di aggiornamento. La classe ReaderWriterLock impone l'accesso esclusivo a una risorsa durante la modifica di tale risorsa da parte di un thread, ma consente accesso non esclusivo per la lettura della risorsa. I blocchi ReaderWriter rappresentano un'alternativa efficace ai blocchi esclusivi, che impongono l'attesa agli altri thread, anche se tali thread non necessitano di aggiornare i dati.

Deadlock

La sincronizzazione dei thread risulta preziosissima nelle applicazioni multithread, ma è possibile che si verifichino deadlock in cui più thread rimangono in reciproca attesa. Un deadlock può essere paragonato a macchine ferme a uno stop di un incrocio in cui ognuno aspetta che l'altro si muova. Evitare i deadlock è fondamentale, la chiave è un'attenta pianificazione. È spesso possibile prevedere situazioni di deadlock creando diagrammi di applicazioni multithread, prima ancora di iniziare la codificazione.

Vedere anche

Concetti

Multithreading avanzato con Visual Basic

Sincronizzazione di thread

Stati di thread

Applicazioni multithread

Riferimenti

System.Threading

Istruzione SyncLock

Altre risorse

Multithreading nei componenti