What is the difference between ManualResetEvent and AutoResetEvent in .NET?

后端 未结 11 2198
慢半拍i
慢半拍i 2020-11-27 09:06

I have read the documentation on this and I think I understand. An AutoResetEvent resets when the code passes through event.WaitOne(), but a ManualResetEvent do

相关标签:
11条回答
  • 2020-11-27 09:29

    OK, normally it does not a good practice to add 2 answers in same thread, but I did not want to edit/delete my previous answer, since it can help on another manner.

    Now, I created, much more comprehensive, and easy to understand, run-to-learn console app snippet below.

    Just run the examples on two different consoles, and observe behaviour. You will get much more clear idea there what is happening behind the scenes.

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

    Auto Reset Event

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

    0 讨论(0)
  • 2020-11-27 09:32

    Yes. It's like the difference between a tollbooth and a door. The ManualResetEvent is the door, which needs to be closed (reset) manually. The AutoResetEvent is a tollbooth, allowing one car to go by and automatically closing before the next one can get through.

    0 讨论(0)
  • 2020-11-27 09:32

    I created simple examples to clarify understanding of ManualResetEvent vs AutoResetEvent.

    AutoResetEvent: lets assume you have 3 workers thread. If any of those threads will call WaitOne() all other 2 threads will stop execution and wait for signal. I am assuming they are using WaitOne(). It is like; if I do not work, nobody works. In first example you can see that

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

    When you call Set() all threads will work and wait for signal. After 1 second I am sending second signal and they execute and wait (WaitOne()). Think about these guys are soccer team players and if one player says I will wait until manager calls me, and others will wait until manager tells them to continue (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 this example you can clearly see that when you first hit Set() it will let all threads go, then after 1 second it signals all threads to wait! As soon as you set them again regardless they are calling WaitOne() inside, they will keep running because you have to manually call Reset() to stop them all.

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

    It is more about Referee/Players relationship there regardless of any of the player is injured and wait for playing others will continue to work. If Referee says wait (Reset()) then all players will wait until next signal.

    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();
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 09:37

    AutoResetEvent maintains a boolean variable in memory. If the boolean variable is false then it blocks the thread and if the boolean variable is true it unblocks the thread.

    When we instantiate an AutoResetEvent object, we pass the default value of boolean value in the constructor. Below is the syntax of instantiate an AutoResetEvent object.

    AutoResetEvent autoResetEvent = new AutoResetEvent(false);
    

    WaitOne method

    This method blocks the current thread and wait for the signal by other thread. WaitOne method puts the current thread into a Sleep thread state. WaitOne method returns true if it receives the signal else returns false.

    autoResetEvent.WaitOne();
    

    Second overload of WaitOne method wait for the specified number of seconds. If it does not get any signal thread continues its work.

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

    We called WaitOne method by passing the 2 seconds as arguments. In the while loop, it wait for the signal for 2 seconds then it continues its work. When the thread got the signal WaitOne returns true and exits the loop and print the "Thread got signal".

    Set method

    AutoResetEvent Set method sent the signal to the waiting thread to proceed its work. Below is the syntax of calling Set method.

    autoResetEvent.Set();
    

    ManualResetEvent maintains a boolean variable in memory. When the boolean variable is false then it blocks all threads and when the boolean variable is true it unblocks all threads.

    When we instantiate a ManualResetEvent, we initialize it with default boolean value.

    ManualResetEvent manualResetEvent = new ManualResetEvent(false);
    

    In the above code, we initialize the ManualResetEvent with false value, that means all the threads which calls the WaitOne method will block until some thread calls the Set() method.

    If we initialize ManualResetEvent with true value, all the threads which calls the WaitOne method will not block and free to proceed further.

    WaitOne Method

    This method blocks the current thread and wait for the signal by other thread. It returns true if its receives a signal else returns false.

    Below is the syntax of calling WaitOne method.

    manualResetEvent.WaitOne();
    

    In the second overload of WaitOne method, we can specify the time interval till the current thread wait for the signal. If within time internal, it does not receives a signal it returns false and goes into the next line of method.

    Below is the syntax of calling WaitOne method with time interval.

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

    We have specify 5 seconds into the WaitOne method. If the manualResetEvent object does not receives a signal between 5 seconds, it set the isSignalled variable to false.

    Set Method

    This method is used for sending the signal to all waiting threads. Set() Method set the ManualResetEvent object boolean variable to true. All the waiting threads are unblocked and proceed further.

    Below is the syntax of calling Set() method.

    manualResetEvent.Set();
    

    Reset Method

    Once we call the Set() method on the ManualResetEvent object, its boolean remains true. To reset the value we can use Reset() method. Reset method change the boolean value to false.

    Below is the syntax of calling Reset method.

    manualResetEvent.Reset();
    

    We must immediately call Reset method after calling Set method if we want to send signal to threads multiple times.

    0 讨论(0)
  • 2020-11-27 09:40

    The short answer is yes. The most important difference is that an AutoResetEvent will only allow one single waiting thread to continue. A ManualResetEvent on the other hand will keep allowing threads, several at the same time even, to continue until you tell it to stop (Reset it).

    0 讨论(0)
提交回复
热议问题