В чем разница между ManualResetEvent и AutoResetEvent в .NET? - PullRequest
501 голосов
/ 30 сентября 2008

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

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

Ответы [ 11 ]

868 голосов
/ 30 сентября 2008

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

115 голосов
/ 30 сентября 2008

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

49 голосов
/ 30 сентября 2008

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

35 голосов
/ 04 декабря 2008

Взято из C # 3.0. Джозеф Албахари

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

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

Эту функцию можно смоделировать с помощью логического поля «gateOpen» (объявленного с ключевым словом volatile) в сочетании с «spin-sleep» - многократно проверяя флаг, а затем спя в течение короткого периода времени.

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

17 голосов
/ 20 июля 2014

Я создал простые примеры, чтобы уточнить понимание ManualResetEvent против 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();
        }
    }
}
11 голосов
/ 23 июня 2015

autoResetEvent.WaitOne()

похож на

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

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

9 голосов
/ 14 апреля 2017

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

Теперь я создал гораздо более понятный и понятный фрагмент приложения для запуска на уроке.

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

Событие ручного сброса

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

7 голосов
/ 11 декабря 2016

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

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

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

Метод WaitOne

Этот метод блокирует текущий поток и ожидает сигнала от другого потока. Метод WaitOne переводит текущий поток в состояние потока Sleep. Метод WaitOne возвращает true, если он получает сигнал, иначе возвращает 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, выходит из цикла и печатает «Поток получил сигнал».

Метод набора

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

autoResetEvent.Set();

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

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

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

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

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

Метод WaitOne

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

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

manualResetEvent.WaitOne();

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

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

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

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

Установить метод

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

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

manualResetEvent.Set();

Метод сброса

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

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

manualResetEvent.Reset();

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

7 голосов
/ 20 апреля 2012

Да, верно.

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

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

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

7 голосов
/ 30 сентября 2008

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

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

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

...