Domanda

Ho letto la documentazione su questo e penso di aver capito. Un AutoResetEvent si ripristina quando il codice passa attraverso event.WaitOne () , ma a ManualResetEvent no.

È corretto?

È stato utile?

Soluzione

Sì. È come la differenza tra un casello e una porta. Il ManualResetEvent è la porta, che deve essere chiusa (ripristinata) manualmente. Il AutoResetEvent è un casello che consente il passaggio di un'auto e la chiusura automatica prima che la successiva possa passare.

Altri suggerimenti

Immagina che AutoResetEvent esegue WaitOne () e Reset () come una singola operazione atomica.

La risposta breve è sì. La differenza più importante è che un AutoResetEvent consentirà a un solo thread in attesa di continuare. D'altra parte, un ManualResetEvent continuerà a consentire ai thread, diversi contemporaneamente, di continuare fino a quando non gli si dice di fermarlo (ripristinalo).

  

Tratto dal libro Nutshell di C # 3.0, di   Joseph Albahari

Threading in C # - E-Book gratuito

Un ManualResetEvent è una variazione di AutoResetEvent. Si differenzia per il fatto che non si reimposta automaticamente dopo che un thread viene lasciato passare su una chiamata WaitOne, e quindi funziona come un gate: chiamando Set apre il gate, consentendo un numero qualsiasi di thread che WaitOne al gate attraversa; la chiamata di Reset chiude il gate, causando, potenzialmente, l'accumulo di una coda di camerieri fino alla successiva apertura.

Si potrebbe simulare questa funzionalità con un booleano "gateOpen" campo (dichiarato con la parola chiave volatile) in combinazione con " spin-sleeping " & # 8211; controllando ripetutamente la bandiera e poi dormendo per un breve periodo di tempo.

ManualResetEvents viene talvolta utilizzato per segnalare che una particolare operazione è stata completata o che l'inizializzazione di un thread è stata completata ed è pronta per eseguire il lavoro.

Ho creato semplici esempi per chiarire la comprensione di ManualResetEvent vs AutoResetEvent .

AutoResetEvent : supponiamo che tu abbia 3 thread di lavoro. Se uno di questi thread chiamerà WaitOne () , tutti gli altri 2 thread interromperanno l'esecuzione e attenderanno il segnale. Suppongo che stiano usando WaitOne () . È come; se non lavoro, nessuno funziona. Nel primo esempio puoi vederlo

autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();

Quando chiami Set () tutti i thread funzioneranno e aspetteranno il segnale. Dopo 1 secondo sto inviando un secondo segnale ed eseguono e attendono ( WaitOne () ). Pensa a questi ragazzi che sono giocatori della squadra di calcio e se un giocatore dice che aspetterò fino a quando il manager mi chiama, e altri aspetteranno fino a quando il manager dice loro di continuare ( Set () )

public class AutoResetEventSample
{
    private AutoResetEvent autoReset = new AutoResetEvent(false);

    public void RunAll()
    {
        new Thread(Worker1).Start();
        new Thread(Worker2).Start();
        new Thread(Worker3).Start();
        autoReset.Set();
        Thread.Sleep(1000);
        autoReset.Set();
        Console.WriteLine("Main thread reached to end.");
    }

    public void Worker1()
    {
        Console.WriteLine("Entered in worker 1");
        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker1 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
    public void Worker2()
    {
        Console.WriteLine("Entered in worker 2");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker2 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
    public void Worker3()
    {
        Console.WriteLine("Entered in worker 3");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker3 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
}

In questo esempio puoi vedere chiaramente che quando premi per la prima volta Set () lascerà andare tutti i thread, quindi dopo 1 secondo segnala a tutti i thread di aspettare! Non appena le imposti nuovamente, indipendentemente dal fatto che stiano chiamando WaitOne () , continueranno a funzionare perché devi chiamare manualmente Reset () per interromperle tutte.

manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();

C'è di più sulla relazione Arbitro / Giocatori lì indipendentemente dal fatto che qualcuno del giocatore sia infortunato e aspettare che gli altri continuino a funzionare. Se l'Arbitro dice aspetta ( Reset () ) tutti i giocatori aspetteranno fino al segnale successivo.

public class ManualResetEventSample
{
    private ManualResetEvent manualReset = new ManualResetEvent(false);

    public void RunAll()
    {
        new Thread(Worker1).Start();
        new Thread(Worker2).Start();
        new Thread(Worker3).Start();
        manualReset.Set();
        Thread.Sleep(1000);
        manualReset.Reset();
        Console.WriteLine("Press to release all threads.");
        Console.ReadLine();
        manualReset.Set();
        Console.WriteLine("Main thread reached to end.");
    }

    public void Worker1()
    {
        Console.WriteLine("Entered in worker 1");
        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker1 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
    public void Worker2()
    {
        Console.WriteLine("Entered in worker 2");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker2 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
    public void Worker3()
    {
        Console.WriteLine("Entered in worker 3");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker3 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
}

autoResetEvent.WaitOne ()

è simile a

try
{
   manualResetEvent.WaitOne();
}
finally
{
   manualResetEvent.Reset();
}

come operazione atomica

OK, normalmente non è una buona pratica aggiungere 2 risposte nello stesso thread, ma non volevo modificare / eliminare la mia risposta precedente, poiché può essere d'aiuto in un altro modo.

Ora ho creato di seguito uno snippet di app console molto più completo e di facile comprensione,

Basta eseguire gli esempi su due diverse console e osservare il comportamento. Avrai un'idea molto più chiara di ciò che sta accadendo dietro le quinte.

Evento di ripristino manuale

using System;
using System.Threading;

namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
    public class ManualResetEventSample
    {
        private readonly ManualResetEvent _manualReset = new ManualResetEvent(false);

        public void RunAll()
        {
            new Thread(Worker1).Start();
            new Thread(Worker2).Start();
            new Thread(Worker3).Start();
            Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
            Thread.Sleep(15000);
            Console.WriteLine("1- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("2- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("3- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("4- Main will call ManualResetEvent.Reset() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("It ran one more time. Why? Even Reset Sets the state of the event to nonsignaled (false), causing threads to block, this will initial the state, and threads will run again until they WaitOne().");
            Thread.Sleep(10000);
            Console.WriteLine();
            Console.WriteLine("This will go so on. Everytime you call Set(), ManualResetEvent will let ALL threads to run. So if you want synchronization between them, consider using AutoReset event, or simply user TPL (Task Parallel Library).");
            Thread.Sleep(5000);
            Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

        }

        public void Worker1()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker1 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker2()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker2 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker3()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker3 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

}

 Output evento reset manuale

Evento ripristino automatico

using System;
using System.Threading;

namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
    public class AutoResetEventSample
    {
        private readonly AutoResetEvent _autoReset = new AutoResetEvent(false);

        public void RunAll()
        {
            new Thread(Worker1).Start();
            new Thread(Worker2).Start();
            new Thread(Worker3).Start();
            Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
            Thread.Sleep(15000);
            Console.WriteLine("1- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("2- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("3- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("4- Main will call AutoResetEvent.Reset() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("Nothing happened. Why? Becasuse Reset Sets the state of the event to nonsignaled, causing threads to block. Since they are already blocked, it will not affect anything.");
            Thread.Sleep(10000);
            Console.WriteLine("This will go so on. Everytime you call Set(), AutoResetEvent will let another thread to run. It will make it automatically, so you do not need to worry about thread running order, unless you want it manually!");
            Thread.Sleep(5000);
            Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

        }

        public void Worker1()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker1 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker2()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker2 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker3()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker3 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

}

 Uscita evento reset automatico

Sì. Questo è assolutamente corretto.

Potresti vedere ManualResetEvent come un modo per indicare lo stato. Qualcosa è acceso (Imposta) o spento (Ripristina). Un evento con una certa durata. Qualsiasi thread in attesa del verificarsi di questo stato può procedere.

Un AutoResetEvent è più paragonabile a un segnale. Un'indicazione one shot che è successo qualcosa. Un evento senza durata. Tipicamente ma non necessariamente il "qualcosa" ciò che è accaduto è piccolo e deve essere gestito da un singolo thread, quindi il ripristino automatico dopo che un singolo thread ha consumato l'evento.

Sì, è vero.

Puoi avere un'idea usando questi due.

Se devi dire che hai finito con un po 'di lavoro e altri (thread) in attesa che questo possa ora procedere, dovresti usare ManualResetEvent.

Se è necessario disporre dell'accesso esclusivo reciproco a qualsiasi risorsa, è necessario utilizzare AutoResetEvent.

AutoResetEvent mantiene una variabile booleana in memoria. Se la variabile booleana è falsa, blocca il thread e se la variabile booleana è true sblocca il thread.

Quando istanziamo un oggetto AutoResetEvent, passiamo il valore predefinito del valore booleano nel costruttore. Di seguito è riportata la sintassi di creare un'istanza di un oggetto AutoResetEvent.

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

Metodo WaitOne

Questo metodo blocca il thread corrente e attende il segnale da un altro thread. Il metodo WaitOne mette il thread corrente in uno stato di thread Sleep. Il metodo WaitOne restituisce true se riceve il segnale, altrimenti restituisce false.

autoResetEvent.WaitOne();

Secondo sovraccarico del metodo WaitOne attendere il numero di secondi specificato. In caso contrario, il thread del segnale continua a funzionare.

static void ThreadMethod()
{
    while(!autoResetEvent.WaitOne(TimeSpan.FromSeconds(2)))
    {
        Console.WriteLine("Continue");
        Thread.Sleep(TimeSpan.FromSeconds(1));
    }

    Console.WriteLine("Thread got signal");
}

Abbiamo chiamato il metodo WaitOne passando i 2 secondi come argomenti. Nel ciclo while, attende il segnale per 2 secondi, quindi continua a funzionare. Quando il thread ha ottenuto il segnale WaitOne ritorna true ed esce dal loop e stampa il messaggio "Thread thread signal".

Imposta metodo

Il metodo Set AutoResetEvent ha inviato il segnale al thread in attesa per continuare il suo lavoro. Di seguito è riportata la sintassi della chiamata del metodo Set.

autoResetEvent.Set();

ManualResetEvent mantiene una variabile booleana in memoria. Quando la variabile booleana è falsa, blocca tutti i thread e quando la variabile booleana è true sblocca tutti i thread.

Quando istanziamo un ManualResetEvent, lo inizializziamo con il valore booleano predefinito.

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

Nel codice sopra, inizializziamo ManualResetEvent con valore falso, ciò significa che tutti i thread che chiamano il metodo WaitOne verranno bloccati fino a quando alcuni thread chiamano il metodo Set ().

Se inizializziamo ManualResetEvent con il valore vero, tutti i thread che chiamano il metodo WaitOne non si bloccheranno e non procederanno liberamente per procedere ulteriormente.

Metodo WaitOne

Questo metodo blocca il thread corrente e attende il segnale da un altro thread. Restituisce vero se riceve un segnale altrimenti restituisce falso.

Di seguito è riportata la sintassi della chiamata del metodo WaitOne.

manualResetEvent.WaitOne();

Nel secondo sovraccarico del metodo WaitOne, possiamo specificare l'intervallo di tempo fino a quando il thread corrente attende il segnale. Se entro il tempo interno, non riceve un segnale restituisce falso e passa alla riga successiva del metodo.

Di seguito è riportata la sintassi della chiamata del metodo WaitOne con intervallo di tempo.

bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));

Abbiamo specificato 5 secondi nel metodo WaitOne. Se l'oggetto manualResetEvent non riceve un segnale tra 5 secondi, imposta la variabile isSignalled su false.

Imposta metodo

Questo metodo viene utilizzato per inviare il segnale a tutti i thread in attesa. Il metodo Set () imposta la variabile booleana dell'oggetto ManualResetEvent su true. Tutti i thread in attesa vengono sbloccati e procedono oltre.

Di seguito è riportata la sintassi della chiamata del metodo Set ().

manualResetEvent.Set();

Metodo di ripristino

Una volta chiamato il metodo Set () sull'oggetto ManualResetEvent, il suo valore booleano rimane vero. Per resettare il valore possiamo usare il metodo Reset (). Il metodo di ripristino modifica il valore booleano in falso.

Di seguito è riportata la sintassi della chiamata del metodo di ripristino.

manualResetEvent.Reset();

Dobbiamo chiamare immediatamente il metodo Reset dopo aver chiamato il metodo Set se vogliamo inviare il segnale ai thread più volte.

Se vuoi capire AutoResetEvent e ManualResetEvent devi capire non il threading ma gli interrupt!

.NET vuole evocare la programmazione di basso livello il più distante possibile.

Un interrupt è qualcosa usato nella programmazione di basso livello che equivale a un segnale che da basso è diventato alto (o viceversa). Quando ciò accade, il programma interrompe la sua normale esecuzione e sposta il puntatore di esecuzione sulla funzione che gestisce questo evento .

La prima cosa da fare quando un errore di interruzione è reimpostare il suo stato, perché l'hardware funziona in questo modo:

  1. un pin è collegato a un segnale e l'hardware attende che cambi (il segnale potrebbe avere solo due stati).
  2. se il segnale cambia significa che qualcosa è successo e l'hardware ha messo una variabile di memoria allo stato (e rimane così anche se il segnale cambia di nuovo).
  3. il programma nota che gli stati di modifica delle variabili e sposta l'esecuzione in una funzione di gestione.
  4. qui la prima cosa da fare, per poter riascoltare questo interrupt, è resettare questa variabile di memoria allo stato non accaduto.

Questa è la differenza tra ManualResetEvent e AutoResetEvent.
Se si verifica un ManualResetEvent e non lo ripristino, la prossima volta non sarò in grado di ascoltarlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top