Pregunta

He leído la documentación sobre esto y creo que entiendo. Un AutoResetEvent se reinicia cuando el código pasa por event.WaitOne () , pero a ManualResetEvent no.

¿Esto es correcto?

¿Fue útil?

Solución

Sí. Es como la diferencia entre una cabina de peaje y una puerta. El ManualResetEvent es la puerta, que debe cerrarse (restablecerse) manualmente. El AutoResetEvent es una cabina de peaje, que permite que pase un auto y se cierra automáticamente antes de que el siguiente pueda pasar.

Otros consejos

Imagina que AutoResetEvent ejecuta WaitOne () y Reset () como una sola operación atómica.

La respuesta corta es sí. La diferencia más importante es que AutoResetEvent solo permitirá que un solo hilo en espera continúe. Por otra parte, un ManualResetEvent seguirá permitiendo que los hilos, varios al mismo tiempo, continúen hasta que le digas que se detenga (restablézcalo).

  

Tomado del libro C # 3.0 de cáscara de nuez, por   Joseph Albahari

Enhebrado en C # - Libro electrónico gratuito

Un ManualResetEvent es una variación de AutoResetEvent. Difiere en que no se restablece automáticamente después de que se deja pasar un hilo en una llamada de WaitOne, y por lo tanto funciona como una puerta: la llamada Set abre la puerta, permitiendo cualquier cantidad de hilos que WaitOne pase por la puerta; La función Restablecer cierra la compuerta, lo que hace que, potencialmente, se acumule una cola de meseros hasta su próxima apertura.

Uno podría simular esta funcionalidad con un booleano " gateOpen " campo (declarado con la palabra clave volátil) en combinación con " spin-sleeping " - Revisar repetidamente la bandera y luego dormir durante un corto período de tiempo.

ManualResetEvents a veces se usa para indicar que una operación en particular está completa, o que la rosca se ha completado y está lista para realizar el trabajo.

Creé ejemplos simples para aclarar la comprensión de ManualResetEvent vs AutoResetEvent .

AutoResetEvent : supongamos que tiene un hilo de 3 trabajadores. Si alguno de esos subprocesos llamará a WaitOne () , los otros 2 subprocesos detendrán la ejecución y esperarán la señal. Supongo que están utilizando WaitOne () . Es como; Si no trabajo, nadie trabaja. En el primer ejemplo puedes ver que

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

Al llamar a Set () , todos los subprocesos funcionarán y esperarán la señal. Después de 1 segundo, estoy enviando la segunda señal y se ejecutan y esperan ( WaitOne () ). Piense que estos jugadores son jugadores de fútbol y si un jugador dice que esperaré hasta que el gerente me llame, y otros esperarán hasta que el administrador les diga que continúen ( 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();
        }
    }
}

En este ejemplo, puedes ver claramente que cuando llegas por primera vez a Set () dejará ir todos los subprocesos, luego, después de 1 segundo, ¡indica que todos los subprocesos esperan! Tan pronto como los vuelva a configurar, independientemente de que estén llamando a WaitOne () , se seguirán ejecutando porque tiene que llamar manualmente a Reset () para detenerlos a todos.

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

Es más sobre la relación del árbitro / jugadores allí, independientemente de que el jugador esté lesionado y esperar a que otros jugadores continúen trabajando. Si el árbitro dice esperar ( Reset () ), todos los jugadores esperarán hasta la próxima señal.

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 ()

es similar a

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

como una operación atómica

De acuerdo, normalmente no es una buena práctica agregar 2 respuestas en el mismo hilo, pero no quise editar / eliminar mi respuesta anterior, ya que puede ayudar de otra manera.

A continuación, he creado un fragmento de aplicación de consola de ejecución para aprender mucho más completo y fácil de entender a continuación.

Simplemente ejecute los ejemplos en dos consolas diferentes y observe el comportamiento. Tendrás una idea mucho más clara de lo que está sucediendo detrás de escena.

Evento de restablecimiento manual

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);
        }
    }

}

 Salida de evento de restablecimiento manual

Evento de reinicio automático

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);
        }
    }

}

 Salida de evento de reinicio automático

Sí. Esto es absolutamente correcto.

Podría ver ManualResetEvent como una manera de indicar el estado. Algo está activado (Establecer) o desactivado (Restablecer). Una ocurrencia con cierta duración. Cualquier subproceso que espera a que suceda ese estado puede continuar.

Un AutoResetEvent es más comparable a una señal. Una indicación de un disparo de que algo ha sucedido. Una ocurrencia sin duración alguna. Normalmente, pero no necesariamente el " algo " lo que sucedió es pequeño y debe ser manejado por un solo hilo, por lo tanto, el restablecimiento automático después de que un solo hilo haya consumido el evento.

Sí, eso es correcto.

Puedes obtener una idea con el uso de estos dos.

Si necesita decir que ya ha terminado con algunos trabajos y otros (subprocesos) en espera de que esto pueda continuar, debe usar ManualResetEvent.

Si necesita tener acceso mutuo exclusivo a cualquier recurso, debe usar AutoResetEvent.

AutoResetEvent mantiene una variable booleana en la memoria. Si la variable booleana es falsa, bloquea el subproceso y si la variable booleana es verdadera, desbloquea el subproceso.

Cuando creamos una instancia de un objeto AutoResetEvent, pasamos el valor predeterminado de valor booleano en el constructor. A continuación se muestra la sintaxis de instanciar un objeto AutoResetEvent.

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

Método WaitOne

Este método bloquea el hilo actual y espera la señal de otro hilo. El método WaitOne pone el hilo actual en un estado de hebra Sleep. El método WaitOne devuelve true si recibe la señal, de lo contrario, devuelve false.

autoResetEvent.WaitOne();

La segunda sobrecarga del método WaitOne espera el número de segundos especificado. Si no recibe ninguna señal, el hilo continúa su trabajo.

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

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

Llamamos al método WaitOne pasando los 2 segundos como argumentos. En el bucle while, espera la señal durante 2 segundos y luego continúa su trabajo. Cuando el subproceso recibió la señal, WaitOne devuelve verdadero y sale del bucle e imprime la " Señal obtenida del subproceso " ;.

Establecer método

El método AutoResetEvent Set envió la señal al hilo en espera para continuar con su trabajo. A continuación se muestra la sintaxis del método Set que llama.

autoResetEvent.Set();

ManualResetEvent mantiene una variable booleana en la memoria. Cuando la variable booleana es falsa, bloquea todos los subprocesos y cuando la variable booleana es verdadera, desbloquea todos los subprocesos.

Cuando creamos una instancia de ManualResetEvent, lo inicializamos con el valor booleano predeterminado.

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

En el código anterior, inicializamos ManualResetEvent con un valor falso, lo que significa que todos los subprocesos que llaman al método WaitOne se bloquearán hasta que algún subproceso llame al método Set ().

Si inicializamos ManualResetEvent con valor verdadero, todos los subprocesos que llaman al método WaitOne no se bloquearán y estarán libres para continuar.

Método WaitOne

Este método bloquea el hilo actual y espera la señal de otro hilo. Devuelve true si recibe una señal o devuelve false.

A continuación se muestra la sintaxis de llamar al método WaitOne.

manualResetEvent.WaitOne();

En la segunda sobrecarga del método WaitOne, podemos especificar el intervalo de tiempo hasta que el hilo actual espere la señal. Si dentro del tiempo interno, no recibe una señal, devuelve falso y pasa a la siguiente línea de método.

A continuación se muestra la sintaxis de llamar al método WaitOne con un intervalo de tiempo.

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

Hemos especificado 5 segundos en el método WaitOne. Si el objeto manualResetEvent no recibe una señal entre 5 segundos, establece la variable isSignalled en falso.

Método de configuración

Este método se utiliza para enviar la señal a todos los hilos en espera. Método Set () establece la variable booleana del objeto ManualResetEvent en true. Todos los hilos en espera se desbloquean y continúan.

A continuación se muestra la sintaxis del método Set () de llamada.

manualResetEvent.Set();

Método de restablecimiento

Una vez que llamamos al método Set () en el objeto ManualResetEvent, su valor booleano permanece verdadero. Para restablecer el valor podemos usar el método Reset (). Restablecer el método cambia el valor booleano a falso.

A continuación se muestra la sintaxis del método de restablecimiento de llamadas.

manualResetEvent.Reset();

Debemos llamar inmediatamente al método de reinicio después de llamar al método de configuración si queremos enviar señales a los hilos varias veces.

Si desea comprender AutoResetEvent y ManualResetEvent, debe comprender, no los subprocesos sino las interrupciones.

.NET quiere evocar la programación de bajo nivel lo más distante posible.

Una interrupción es algo que se usa en la programación de bajo nivel que equivale a una señal que desde bajo se convirtió en alta (o viceversa). Cuando esto sucede, el programa interrumpe su ejecución normal y mueve el puntero de ejecución a la función que maneja este evento .

Lo primero que se debe hacer cuando ocurre una interrupción es restablecer su estado, ya que el hardware funciona de esta manera:

  1. un pin está conectado a una señal y el hardware lo escucha para que cambie (la señal podría tener solo dos estados).
  2. si la señal cambia significa que algo sucedió y el hardware colocó una variable de memoria en el estado que ocurrió (y seguirá siendo así, incluso si la señal cambia nuevamente).
  3. el programa nota que la variable cambia de estado y mueve la ejecución a una función de manejo.
  4. aquí lo primero que debe hacer, para poder escuchar de nuevo esta interrupción, es restablecer esta variable de memoria al estado no ocurrido.

Esta es la diferencia entre ManualResetEvent y AutoResetEvent.
Si se produce un ManualResetEvent y no lo reinicio, la próxima vez que ocurra no podré escucharlo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top