В чем разница между ManualResetEvent и AutoResetEvent in.NET?
Я прочитал документацию по этому вопросу, и я думаю, что понимаю. Ан AutoResetEvent сбрасывается, когда код проходит через event.WaitOne(), а ManualResetEvent нет.
это правильно?
10 ответов:
да. Это как разница между будкой и дверью. Элемент
ManualResetEventЭто дверь, которую нужно закрыть (сбросить) вручную. ЭлементAutoResetEvent- это будка для сбора пошлин, позволяющая одному автомобилю проходить и автоматически закрываться до того, как следующий сможет пройти.
только представьте себе, что
AutoResetEventвыполняетWaitOne()иReset()как одна атомарная операция.
короткий ответ-да. Самое важное отличие заключается в том, что AutoResetEvent позволит продолжить только один ожидающий поток. С другой стороны, ManualResetEvent будет продолжать разрешать потоки, несколько одновременно даже, пока вы не скажете ему остановиться (сбросить его).
взято из книги C# 3.0 Nutshell, by Джозеф Албахари
резьба в C# - бесплатная электронная книга
A ManualResetEvent-это вариант AutoResetEvent. Он отличается тем, что он не сбрасывается автоматически после того, как поток пропускается при вызове WaitOne, и поэтому функционирует как ворота: вызывающий набор открывает ворота, позволяя любое количество потоков, которые ждут у ворот; вызывающий сброс закрывает ворота, вызывая, потенциально, a очередь официантов накапливалась до ее следующего открытия.
можно смоделировать эту функциональность с помощью логического поля " 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();речь идет больше об отношениях рефери / игроков там независимо от того, кто из игроков травмирован и ждет, когда другие будут продолжать работать. Если судья говорит ждать (сброс ();), то все игроки будут ждать до следующего сигнала.
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, то он блокирует поток, и если значение логической переменной истинна это разблокирует поток.
когда мы создаем экземпляр объекта AutoResetEvent, мы передаем значение по умолчанию boolean value в конструкторе. Ниже приведен синтаксис создания экземпляра объекта 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 секунды в качестве аргументов. В то время как цикл, он ждет сигнала для 2 секунд потом продолжает свою работу. Когда поток получил сигнал WaitOne возвращает true и выходит из цикла и печатает "поток получил сигнал".
метод set
установить способ AutoResetEvent послал сигнал на поток, ожидающий, чтобы продолжить свою работу. Ниже приведен синтаксис вызова метода set.
autoResetEvent.Set();
ManualResetEvent сохраняет логическую переменную в памяти. Когда булева переменная имеет значение false, она блокируется все потоки и когда логическая переменная имеет значение true, она разблокирует все потоки.
когда мы создаем экземпляр ManualResetEvent, мы инициализируем его с логическим значением по умолчанию.
ManualResetEvent manualResetEvent = new ManualResetEvent(false);в приведенном выше коде мы инициализируем ManualResetEvent с ложным значением, что означает, что все потоки, вызывающие метод WaitOne, будут блокироваться до тех пор, пока какой-либо поток не вызовет метод Set ().
если мы инициализируем ManualResetEvent с истинным значением, все потоки, которые вызывают WaitOne способ не будет блокировать и свободно двигаться дальше.
Метод WaitOne
этот метод блокирует текущий поток и ждать сигнала от другого потока. Он возвращает true, если его получает сигнал else возвращает false.
ниже приведен синтаксис вызова метода WaitOne.
manualResetEvent.WaitOne();во второй перегрузке метода WaitOne мы можем указать интервал времени, пока текущий поток не дождется сигнала. Если в пределах времени внутреннего, он не получает сигнал, он возвращает false и переходит в следующую строку метод.
Ниже приведен синтаксис вызова метода WaitOne с интервалом времени.
bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));мы должны указать 5 секунд в метод WaitOne. Если объект manualResetEvent не получает сигнал в течение 5 секунд, он устанавливает переменную isSignalled в значение false.
Метод Set
этот метод используется для отправки сигнала всем ожидающим потокам. Set () Набор методов логическая переменная объекта ManualResetEvent равна true. Все ожидающие потоки разблокированы и продолжаются дальше.
Ниже приведен синтаксис вызова метода set ().
manualResetEvent.Set();Сброс Способ
как только мы вызываем метод Set() для объекта ManualResetEvent, его логическое значение остается true. Для сброса значения мы можем использовать метод Reset (). Метод сброса измените логическое значение на false.
ниже приведен синтаксис вызова Reset метод.
manualResetEvent.Reset();мы должны немедленно вызвать метод Reset после вызова метода Set, если мы хотим отправить сигнал потокам несколько раз.
ОК, обычно это не очень хорошая практика, чтобы добавить 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); } } }Событие Автоматического Сброса
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); } } }
да. Это абсолютно правильно.
вы можете увидеть ManualResetEvent как способ указать состояние. Что-то включено (установлено) или выключено (сброшено). Событие с некоторой длительностью. Любой поток, ожидающий этого состояния, может продолжить работу.
AutoResetEvent более сопоставим с сигналом. Один выстрел указывает на то, что что-то произошло. Событие без какой-либо продолжительности. Как правило, но не обязательно "что-то", что произошло, мало и должно быть обрабатывается одним потоком-следовательно, автоматический сброс после того, как один поток потребил событие.


Comments