




Краткий ответ: да. Наиболее важным отличием является то, что AutoResetEvent позволит продолжить работу только одному ожидающему потоку. С другой стороны, ManualResetEvent будет позволять потокам, даже нескольким одновременно, продолжать работу, пока вы не скажете ему остановиться (сбросить его).
да. Это как разница между будкой и дверью. ManualResetEvent - это дверь, которую необходимо закрыть (сбросить) вручную. AutoResetEvent - это пункт взимания платы за проезд, позволяющий проезжать одну машину и автоматически закрывающийся, прежде чем проехать следующий.
Хуже того, не ждите слишком долго, чтобы установить ARE на WaitOne, или тем временем он будет сброшен. Было много заброшенных тем с этим.
Или как дверь и турникет.
Ой, поэтому их называют такими, какие они есть.
@DanGoldstein ну, так как это не закрыто и на случай, если кто-то еще захочет: msdn.microsoft.com/en-us/library/…
Вы только что объяснили разницу между словами «автоматический» и «ручной».
ManualResetEvent похож на дверь, все потоки в ситуации waitone будут освобождены после вызова set до тех пор, пока не будет выполнен сброс в следующий момент.
да. Это абсолютно правильно.
Вы можете видеть ManualResetEvent как способ указать состояние. Что-то включено (Установить) или выключено (Сброс). Событие с некоторой продолжительностью. Любой поток, ожидающий наступления этого состояния, может продолжить работу.
AutoResetEvent более сопоставим с сигналом. Одноразовый индикатор того, что что-то произошло. Событие без какой-либо продолжительности. Обычно, но не обязательно, «что-то», что произошло, невелико и должно обрабатываться одним потоком - отсюда автоматический сброс после того, как один поток поглотил событие.
Только представьте, что AutoResetEvent выполняет WaitOne() и Reset() как одну атомарную операцию.
За исключением того, что если вы выполнили WaitOne и Reset как одну атомарную операцию с событием ManualResetEvent, он все равно будет делать что-то отличное от AutoResetEvent. ManualResetEvent освобождает все ожидающие потоки одновременно, тогда как AutoResetEvent гарантирует освобождение только одного ожидающего потока.
Taken from C# 3.0 Nutshell book, by Joseph Albahari
Многопоточность в C# - Бесплатная электронная книга
ManualResetEvent - это разновидность AutoResetEvent. Он отличается тем, что он не сбрасывается автоматически после того, как поток пропускается при вызове WaitOne, и поэтому функционирует как вентиль: вызов Set открывает вентиль, позволяя любому количеству потоков, через которые проходит WaitOne на шлюзе; вызов Reset закрывает ворота, что потенциально может привести к накоплению очереди официантов до следующего открытия.
Можно смоделировать эту функциональность с помощью логического поля «gateOpen» (объявленного с помощью ключевого слова volatile) в сочетании с «spin-sleep» - многократная проверка флага, а затем засыпание на короткий период времени.
ManualResetEvents иногда используются, чтобы сигнализировать, что определенная операция завершена или что поток завершил инициализацию и готов к выполнению работы.
Да все верно.
Вы можете получить представление об использовании этих двух.
Если вам нужно сообщить, что вы закончили некоторую работу и другие (потоки), ожидающие этого, теперь могут продолжить, вы должны использовать ManualResetEvent.
Если вам нужен взаимоисключающий доступ к какому-либо ресурсу, вы должны использовать AutoResetEvent.
Я создал простые примеры, чтобы прояснить понимание 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();
}
}
}
autoResetEvent.WaitOne()
похож на
try
{
manualResetEvent.WaitOne();
}
finally
{
manualResetEvent.Reset();
}
как атомарная операция
Это правильно только концептуально, но не практически. Между WaitOne и Reset может произойти переключение контекста; это может привести к незаметным ошибкам.
Не могли бы вы проголосовать за это сейчас? Второй блок кода здесь практически никто делать не будет, дело в понимании разницы.
AutoResetEvent поддерживает логическую переменную в памяти. Если логическая переменная ложна, то поток блокируется, а если логическая переменная истинна, поток разблокируется.
Когда мы создаем экземпляр объекта AutoResetEvent, мы передаем в конструктор значение логического значения по умолчанию. Ниже приведен синтаксис создания экземпляра объекта AutoResetEvent.
AutoResetEvent autoResetEvent = new AutoResetEvent(false);
Метод WaitOne
Этот метод блокирует текущий поток и ожидает сигнала от другого потока. Метод WaitOne переводит текущий поток в состояние спящего потока. Метод 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 возвращает истину, выходит из цикла и печатает «Поток получил сигнал».
Установить метод
Метод AutoResetEvent Set отправил сигнал ожидающему потоку, чтобы он продолжил свою работу. Ниже приведен синтаксис вызова метода Set.
autoResetEvent.Set();
ManualResetEvent поддерживает логическую переменную в памяти. Когда логическая переменная имеет значение false, она блокирует все потоки, а когда логическая переменная имеет значение true, она разблокирует все потоки.
Когда мы создаем экземпляр ManualResetEvent, мы инициализируем его логическим значением по умолчанию.
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
В приведенном выше коде мы инициализируем ManualResetEvent со значением false, что означает, что все потоки, вызывающие метод WaitOne, будут блокироваться до тех пор, пока какой-либо поток не вызовет метод Set ().
Если мы инициализируем ManualResetEvent со значением true, все потоки, вызывающие метод WaitOne, не будут блокироваться и смогут продолжить работу.
Метод WaitOne
Этот метод блокирует текущий поток и ожидает сигнала от другого потока. Он возвращает истину, если получает сигнал, иначе возвращает ложь.
Ниже приведен синтаксис вызова метода WaitOne.
manualResetEvent.WaitOne();
Во второй перегрузке метода WaitOne мы можем указать временной интервал, в течение которого текущий поток ожидает сигнала. Если в течение времени internal, он не получает сигнала, он возвращает 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, если мы хотим отправить сигнал потокам несколько раз.
Хорошо, обычно не рекомендуется добавлять 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);
}
}
}
Событие автоматического сброса
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);
}
}
}
это был лучший способ все это понять, скопировал код и запустил все, изменив несколько вещей, теперь хорошо понял
Если вы хотите понять AutoResetEvent и ManualResetEvent, вам нужно понимать не потоки, а прерывания!
.NET хочет вызвать в воображении низкоуровневое программирование как можно более отдаленное.
Прерывания - это то, что используется в низкоуровневом программировании, что равносильно сигналу, который с низкого уровня стал высоким (или наоборот). Когда это происходит, программа прерывает свое обычное выполнение и перемещает указатель выполнения на функцию, которая обрабатывает этот мероприятие.
Первое, что нужно сделать при возникновении прерывания, - это изменить его состояние сброс настроек, потому что оборудование работает следующим образом:
В этом разница между ManualResetEvent и AutoResetEvent.
Если произойдет событие ManualResetEvent, и я не сброшу его, в следующий раз, когда это произойдет, я не смогу его прослушать.
Пусть этот av поможет понять разницу youtube.com/watch?v=xaaRBh07N34