Un ManualResetEvent
viene utilizzato per poter lanciare segnali tra thread diversi, in particolare server per notificare a uno o piΓΉ thread in attesa che si Γ¨ verificato un evento.
Creazione
Un ManualResetEvent
mantiene internamente una variabile booleana, quando la variabile Γ¨ false questo blocca tutti i thread (che sono in attesa utilizzando il metodo WaitOne()
, quando viene impostata a true invece permette a chi Γ¨ in attesa di procedere.
Al costruttore nel ManualResetEvent
viene indicato il suo stato iniziale, tipicamente false (quindi bloccante)
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
WaitOne
Il metodo WaitOne()
blocca il thread corrente fino a che un altro thread non invii un segnale di sblocco, ritorna quindi true se riceve un segnale, false altrimenti.
manualResetEvent.WaitOne();
Eβ possibile inoltre indicare un tempo massimo di attesa per il segnale, utilizzando un overload del metodo:
bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
Se il manualResetEvent
non riceve un segnale entro 5 secondi, la variabile isSignalled
viene impostata a false
.
WaitOne(0)
WaitOne(0)
non aspetta alcun segnale, rimane bloccante fino a che la variabile interna Γ¨ false e appena diventa true si sblocca.
Viene utilizzato per autosincronizzare un singolo thread (vedi esempio).
Set
Il metodo Set()
viene utilizzato per inviare un segnale a tutti i thread che stanno aspettando; questo viene impostato settando la variabile booleana interna a true. Tutti i thread aspettanti sono sbloccati e possono procedere.
manualResetEvent.Set();
Reset
Una volta chiamato il metodo Set()
sullβoggetto manualResetEvent
, il suo valore booleano rimane a true. Per resettare il suo valore al valore iniziale (false
) possiamo utilizzare il metodo Reset()
.
manualResetEvent.Reset();
Se vogliamo inviare un segnale tra thread piΓΉ volte Γ¨ indispensabile chiamare il metodo Reset
.
Esempio
Nella Stazione.cs
voglio un segnale per il fermo del ciclo che mi permette di uscire dal ciclo macchina a richiesta.
Creo il seguente ManualResetEvent
:
protected ManualResetEvent StopCicloSignal = new ManualResetEvent(false);
Nella funzione Ciclo()
inserisco il seguente codice:
if (!StopCicloSignal.IsValid() || StopCicloSignal.WaitOne(0))
{
return false;
}
Questo codice mi permette di uscire dalla funzione qualora StopCicloSignal
diventi true
, quindi quando qualcuno (in questo caso internamente) chiami la funzione StopCicloSignal.Set()
.
Questo Γ¨ il metodo StopCiclo() che infatti effettua
StopCicloSignal.Set();
interrompendo così (non istantaneamente ma appena il codice arriva al controllo sul while) il ciclo.
Esempio 2
private ManualResetEvent _tryToLoadInProgress;
public LatestRejectionUi(MultiLanguage lang) : base(lang)
{
_tryToLoadInProgress = new ManualResetEvent(false);
}
private bool TryToLoadImage()
{
_tryToLoadInProgress.Set();
// Codice complesso che deve essere acceduto da un thread alla volta
_tryToLoadInProgress.Reset();
}
private void HandleTimer(object sender, ElapsedEventArgs e)
{
if (_tryToLoadInProgress.WaitOne(0))
return;
TryToLoadImage()
}