Jaka jest różnica między ManualResetEvent i AutoResetEvent in.NET?

Przeczytałem dokumentację na ten temat i myślę, że rozumiem. Na AutoResetEvent resetuje się, gdy kod przechodzi przez event.WaitOne(), ale ManualResetEvent nie.

Czy to prawda?
Author: Mark Byers, 2008-09-30

10 answers

Tak. To jak różnica między bramą a drzwiami. ManualResetEvent to drzwi, które należy ręcznie zamknąć (zresetować). [1]} jest bramą płatną, która umożliwia przejechanie jednego samochodu i automatyczne zamknięcie przed przejazdem następnego.

 835
Author: Dan Goldstein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-07 10:39:25

Wyobraź sobie, że AutoResetEvent wykonuje WaitOne() i Reset() jako pojedynczą operację atomową.

 111
Author: Michael Damatov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-22 09:17:05

Krótka odpowiedź brzmi: tak. Najważniejszą różnicą jest to, że AutoResetEvent pozwoli na kontynuowanie tylko jednego oczekującego wątku. Z drugiej strony ManualResetEvent będzie nadal pozwalał wątkom, nawet kilku jednocześnie, kontynuować, dopóki nie powiesz mu, aby przestał (zresetuj go).

 46
Author: Martin Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 16:59:22

Zaczerpnięte z C # 3.0 Nutshell book, przez Joseph Albahari

Threading w C# - darmowy e-Book

ManualResetEvent jest odmianą AutoResetEvent. Różni się tym, że nie resetuje się automatycznie po przepuszczeniu wątku podczas połączenia WaitOne, a więc działa jak Brama: zestaw wywołujący otwiera bramę, pozwalając dowolnej liczbie wątków, które czekają na bramce; Reset wywołujący zamyka bramę, powodując potencjalnie gromadzenie się kolejki kelnerów aż do następnego otwarcia.

Można symulować tę funkcjonalność za pomocą boolowskiego pola " gateOpen "(zadeklarowanego słowem kluczowym volatile) w połączeniu z" spin-sleeping " – wielokrotne sprawdzanie flagi, a następnie uśpienie przez krótki okres czasu.

ManualResetEvents są czasami używane, aby zasygnalizować, że dana operacja jest zakończona lub że wątek zakończył inicjalizację i jest gotowy do wykonania pracy.

 34
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-04 07:47:25

Stworzyłem proste przykłady, aby wyjaśnić zrozumienie ManualResetEvent vs AutoResetEvent.

AutoResetEvent: Załóżmy, że masz 3 wątki workerów. Jeśli któryś z tych wątków wywoła metodę WaitOne (), wszystkie pozostałe 2 wątki zatrzymają wykonanie i poczekają na sygnał. Zakładam, że używają WaitOne (). Jeśli ja nie pracuję, to nikt nie pracuje. W pierwszym przykładzie widać, że

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

Po wywołaniu Set (); wszystkie wątki będą działać i czekać na sygnał. Po 1 sek wysyłam drugi sygnał i wykonują i czekają (WaitOne ();). Pomyśl, że ci goście są piłkarzami i jeśli jeden gracz powie, że poczekam, aż menedżer zadzwoni do mnie, a inni poczekają, aż menedżer powie im, aby kontynuowali (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();
            }
        }
    }

W tym przykładzie widać wyraźnie, że po pierwszym naciśnięciu Set(); spowoduje to odejście wszystkich wątków, a następnie po 1 sek sygnalizuje wszystkim wątkom oczekiwanie! Jak tylko ustawisz je ponownie, niezależnie od tego, czy wywołują WaitOne () wewnątrz, będą działać, ponieważ musisz ręcznie wywołać Reset (), aby zatrzymać je wszystkie.

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

Chodzi bardziej o relację sędzia / gracze niezależnie od tego, czy któryś z graczy jest kontuzjowany i czekać na grę inni będą nadal pracować. Jeśli sędzia powie wait (Reset ();) wtedy wszyscy gracze będą czekać do następnego sygnału.

    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();
            }
        }
    }
 18
Author: Teoman shipahi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-20 19:18:45

autoResetEvent.WaitOne()

Jest podobny do

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

Jako operacja atomowa

 8
Author: vezenkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-13 08:04:22

Tak, to prawda.

Można uzyskać pomysł za pomocą tych dwóch.

Jeśli chcesz powiedzieć, że zakończyłeś jakąś pracę i inne (wątki) czekające na to mogą teraz kontynuować, powinieneś użyć ManualResetEvent.

Jeśli potrzebujesz wzajemnego wyłącznego dostępu do dowolnego zasobu, powinieneś użyć AutoResetEvent.

 7
Author: Swapnil Patil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-20 08:48:18

AutoResetEvent utrzymuje zmienną logiczną w pamięci. Jeśli zmienna logiczna jest false, to blokuje wątek, a jeśli zmienna logiczna jest true, odblokowuje wątek.

Kiedy tworzymy instancję obiektu AutoResetEvent, przekazujemy domyślną wartość boolean value w konstruktorze. Poniżej znajduje się składnia instantiate obiektu AutoResetEvent.

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

Metoda WaitOne

Ta metoda blokuje bieżący wątek i czeka na sygnał przez inny wątek. Metoda WaitOne umieszcza bieżący wątek w stanie uśpienia. Metoda WaitOne zwraca true jeżeli otrzyma sygnał else zwraca false.

autoResetEvent.WaitOne();

Drugie przeciążenie metody WaitOne czekać na określoną liczbę sekund. Jeśli nie otrzyma żadnego sygnału wątek kontynuuje swoją pracę.

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

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

Wywołaliśmy metodę WaitOne przekazując 2 sekundy jako argumenty. W pętli while czeka na sygnał przez 2 sekundy, a następnie kontynuuje pracę. Kiedy wątek dostał signal WaitOne zwraca true i wychodzi z pętli i wypisuje "thread got signal".

Metoda Set

Metoda AutoResetEvent Set wysłała sygnał do oczekującego wątku, aby kontynuować swoją pracę. Poniżej znajduje się składnia wywołania metody Set.

autoResetEvent.Set();

ManualResetEvent utrzymuje zmienną logiczną w pamięci. Gdy zmienna logiczna jest false, to blokuje wszystkie wątki, a gdy zmienna logiczna jest true, odblokowuje wszystkie wątki.

Kiedy tworzymy instancję ManualResetEvent, inicjalizujemy ją z domyślną wartością logiczną.

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

W powyższym kodzie inicjalizujemy ManualResetEvent wartością false, co oznacza, że wszystkie wątki, które wywołują metodę WaitOne, będą blokowane, dopóki jakiś wątek nie wywoła metody Set ().

Jeśli zainicjalizujemy ManualResetEvent z wartością true, wszystkie wątki, które wywołują metodę WaitOne, nie będą blokować i będą mogły kontynuować.

Metoda WaitOne

Ta metoda blokuje bieżący wątek i czeka na sygnał przez inny wątek. Zwraca true jeśli its odbiera sygnał else zwraca false.

Poniżej znajduje się składnia wywołania metody WaitOne.

manualResetEvent.WaitOne();

W drugim przeciążeniu metody WaitOne możemy określić przedział czasu, aż bieżący wątek będzie czekał na sygnał. Jeśli w czasie wewnętrznym nie otrzyma sygnału zwraca false i przechodzi do następnej linii metody.

Poniżej znajduje się składnia wywołania metody WaitOne z przedziałem czasowym.

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

Mamy 5 sekund do metody WaitOne. Jeżeli obiekt manualResetEvent nie odbierze sygnału pomiędzy 5 sekundami, ustawia zmienną isSignalled na false.

Metoda Set

Ta metoda jest używana do wysyłania sygnału do wszystkich oczekujących wątków. Metoda Set () ustawia zmienną boolean obiektu ManualResetEvent na true. Wszystkie oczekujące wątki są odblokowywane i kontynuowane.

Poniżej znajduje się składnia wywołania zestawu() metoda.

manualResetEvent.Set();

Metoda Resetowania

Po wywołaniu metody Set() w obiekcie ManualResetEvent jej wartość logiczna pozostaje prawdziwa. Aby zresetować wartość możemy użyć metody Reset (). Metoda Reset zmienia wartość logiczną na false.

Poniżej znajduje się składnia wywołania metody Reset.

manualResetEvent.Reset();

Musimy natychmiast wywołać metodę Reset po wywołaniu metody Set, jeśli chcemy wysłać sygnał do wątków wiele razy.

 7
Author: Masoud Siahkali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-11 10:38:19

Ok, normalnie nie jest dobrą praktyką dodawanie 2 odpowiedzi w tym samym wątku, ale nie chciałem edytować/usuwać mojej poprzedniej odpowiedzi, ponieważ może pomóc w inny sposób.

Teraz stworzyłem znacznie bardziej obszerny i łatwy do zrozumienia fragment aplikacji konsolowej run-to-learn poniżej.

Po prostu uruchom przykłady na dwóch różnych konsolach i obserwuj zachowanie. Otrzymasz znacznie bardziej jasny obraz tego, co dzieje się za kulisami.

Reset Ręczny Event

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

}

Wyjście Zdarzenia Reset Ręczny

Zdarzenie Auto Reset

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

}

Wyjście Zdarzenia Automatycznego Resetowania

 7
Author: Teoman shipahi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-14 15:02:07

Tak. To jest absolutnie poprawne.

Możesz zobaczyć ManualResetEvent jako sposób wskazania stanu. Coś jest włączone (ustawione) lub wyłączone (resetowane). Zdarzenie z pewnym czasem trwania. Każdy wątek czekający na ten stan może kontynuować.

AutoResetEvent jest bardziej porównywalny do sygnału. Jeden strzał wskazuje, że coś się stało. Zdarzenie bez czasu trwania. Zazwyczaj, ale niekoniecznie" coś", co się stało, jest małe i musi być obsługiwane przez jednego thread-stąd automatyczny reset po zużyciu zdarzenia przez pojedynczy wątek.

 6
Author: Boaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 17:36:05