В чем разница между ManualResetEvent и AutoResetEvent in.NET?

Я прочитал документацию по этому вопросу, и я думаю, что понимаю. Ан AutoResetEvent сбрасывается, когда код проходит через event.WaitOne(), но a ManualResetEvent нет.

это правильно?

10 ответов


да. Это как разница между будкой и дверью. The ManualResetEvent - Это дверь, которую необходимо закрыть (сбросить) вручную. The AutoResetEvent - это будка, позволяющая одному автомобилю пройти и автоматически закрываться, прежде чем следующий сможет пройти.


только представьте, что AutoResetEvent выполняет WaitOne() и Reset() как одна атомарная операция.


короткий ответ-да. Самое важное отличие заключается в том, что AutoResetEvent позволит продолжить только один поток ожидания. С другой стороны, ManualResetEvent будет продолжать позволять потокам, нескольким одновременно, продолжать, пока вы не скажете ему остановиться (сбросить его).


взято из C# 3.0 Nutshell book, by Джозеф Albahari

Threading в C# - бесплатная электронная книга

ManualResetEvent-это вариант AutoResetEvent. Он отличается тем, что он не сбрасывается автоматически после того, как поток пропускается по вызову WaitOne, и поэтому функционирует как ворота: вызывающий набор открывает ворота, позволяя любому количеству потоков, которые WaitOne в воротах; вызывающий сброс закрывает ворота, что потенциально может привести к очередь официантов накапливалась до следующего открытия.

можно смоделировать эту функциональность с помощью логического поля " gateOpen "(объявленного с ключевым словом volatile) в сочетании с" спин-спящим " – многократной проверкой флага, а затем спящим в течение короткого периода времени.

ManualResetEvents иногда используются, чтобы сигнализировать, что определенная операция завершена или что инициализация потока завершена и готова к выполнению работы.


Я создал простые примеры, чтобы уточнить понимание ManualResetEvent vs AutoResetEvent.

AutoResetEvent: предположим, у вас есть 3 рабочих потока. Если какой-либо из этих потоков вызовет WaitOne (), все остальные 2 потока остановят выполнение и будут ждать сигнала. Я предполагаю, что они используют WaitOne(). Это как, если я не работаю, никто не работает. В первом примере вы можете увидеть, что

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

когда вы вызываете Set (); все потоки будут работать и ждать сигнала. После 1 сек. Я посылаю второй сигнал, и они выполняют и ждут (WaitOne ();). Подумайте об этих парнях-футболистах, и если один игрок говорит, что я буду ждать, пока менеджер позвонит мне, а другие будут ждать, пока менеджер скажет им продолжить (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();
            }
        }
    }

в этом примере вы можете ясно видеть, что при первом нажатии Set (); он отпустит все потоки, а затем через 1 сек он сигнализирует всем потокам ждать! Как только вы установите их снова, независимо от того, что они вызывают WaitOne () внутри, они будут продолжайте работать, потому что вам нужно вручную вызвать Reset (), чтобы остановить их все.

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

это больше о рефери / игроки отношения там, независимо от любого из игроков травмирован и ждать, пока играть другие будут продолжать работать. Если судья говорит ждать (Reset ();), то все игроки будут ждать до следующего сигнала.

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

похож на

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

как атомарная операция


Да, это верно.

вы можете получить представление об использовании этих двух.

Если вам нужно сказать, что вы закончили какую-то работу и другие (потоки), ожидающие этого, теперь могут продолжаться, вы должны использовать ManualResetEvent.

Если вам нужен взаимный эксклюзивный доступ к любому ресурсу, вы должны использовать AutoResetEvent.


AutoResetEvent сохраняет логическую переменную в памяти. Если логическая переменная имеет значение false, она блокирует поток, а если логическая переменная имеет значение true, она разблокирует поток.

когда мы создаем экземпляр объекта AutoResetEvent, мы передаем значение по умолчанию логического значения в конструкторе. Ниже приведен синтаксис создания экземпляра объекта AutoResetEvent.

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

метод WaitOne

этот метод блокирует текущий поток и ждать сигнала от другого потока. Метод WaitOne помещает текущий поток в состояние спящего потока. Метод WaitOne возвращает true, если он получает сигнал else возвращает false.

autoResetEvent.WaitOne();

вторая перегрузка метода WaitOne подождите указанное количество секунд. Если он не получает никакого сигнала, поток продолжает свою работу.

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

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

мы вызвали метод WaitOne, передав 2 секунды в качестве аргументов. В цикле while он ждет сигнала для 2 секунд потом продолжает свою работу. Когда поток получил сигнал WaitOne возвращает true и выходит из цикла и печатает "поток получил сигнал".

метод set

метод AutoResetEvent Set отправил сигнал ожидающему потоку для продолжения его работы. Ниже приведен синтаксис вызова метода set.

autoResetEvent.Set();

ManualResetEvent сохраняет логическую переменную в памяти. Когда логическая переменная false, она блокирует все потоки и когда логическая переменная имеет значение true, она разблокирует все потоки.

когда мы создаем экземпляр ManualResetEvent, мы инициализируем его логическим значением по умолчанию.

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

в приведенном выше коде мы инициализируем ManualResetEvent со значением false, что означает, что все потоки, вызывающие метод WaitOne, будут блокировать, пока какой-либо поток не вызовет метод Set ().

если мы инициализируем ManualResetEvent с истинным значением, все потоки, которые вызывают WaitOne способ не будет блокировать и свободно двигаться дальше.

Метод WaitOne

этот метод блокирует текущий поток и ждать сигнала от другого потока. Он возвращает true, если его получает сигнал, иначе возвращает false.

Ниже приведен синтаксис вызова метода WaitOne.

manualResetEvent.WaitOne();

во второй перегрузке метода WaitOne мы можем указать интервал времени, пока текущий поток не дождется сигнала. Если в пределах времени внутреннего, он не получает сигнал, он возвращает false и переходит в следующую строку метод.

Ниже приведен синтаксис вызова метода waitone с временным интервалом.

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

у нас есть указать 5 секунд в метод WaitOne. Если объект manualResetEvent не получает сигнал в течение 5 секунд, он устанавливает для переменной isSignalled значение false.

Set Method

этот метод используется для отправки сигнала всем ожидающим потокам. Set () метод set логическая переменная объекта ManualResetEvent имеет значение true. Все ожидающие потоки разблокируются и дальше.

Ниже приведен синтаксис вызова метода set ().

manualResetEvent.Set();

Сброс Способ

как только мы вызываем метод Set() для объекта ManualResetEvent, его логическое значение остается истинным. Чтобы сбросить значение, мы можем использовать метод Reset (). Метод Reset измените логическое значение на false.

Ниже приведен синтаксис вызова Reset метод.

manualResetEvent.Reset();

мы должны немедленно вызвать метод Reset после вызова метода Set, если мы хотим отправить сигнал потокам несколько раз.


OK, обычно не рекомендуется добавлять 2 ответа в один поток, но я не хотел редактировать/удалять свой предыдущий ответ, так как он может помочь другим способом.

теперь я создал, гораздо более полный и простой для понимания, фрагмент консольного приложения run-to-learn ниже.

просто запустите примеры на двух разных консолях и наблюдайте за поведением. Вы получите гораздо более четкое представление о том, что происходит за кулисами.

руководство Сброс События

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

}

Manual Reset Event Output

Событие Автоматического Сброса

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

}

Auto Reset Event Output


да. Это абсолютно правильно.

вы можете увидеть ManualResetEvent как способ указать состояние. Что-то включено (установлено) или выключено (сброшено). Событие с некоторой продолжительностью. Любой поток, ожидающий этого состояния, может продолжаться.

AutoResetEvent более сопоставим с сигналом. Один выстрел указывает на то, что что-то случилось. Событие без какой-либо продолжительности. Как правило, но не обязательно" что-то", что произошло, мало и должно быть обрабатывается одним потоком-следовательно, автоматический сброс после того, как один поток потребил событие.