.NET中的ManualResetEvent和AutoResetEvent有什么区别?
我已经阅读了这个文件,我想我明白了。 当代码通过event.WaitOne()
, AutoResetEvent
重置,但ManualResetEvent
不会。
它是否正确?
是。 这就像收费站和门的区别。 ManualResetEvent
是需要手动closures(重置)的门。 AutoResetEvent
是一个收费站,允许一辆车通过并在下一个可以通过之前自动closures。
试想一下, AutoResetEvent
WaitOne()
和Reset()
作为一个primefaces操作来执行。
简短的答案是肯定的。 最重要的区别是AutoResetEvent只允许一个等待线程继续。 另一方面,ManualResetEvent将继续允许多个线程在同一时间继续,直到您告诉它停止(重置它)。
采取从C#3.0果壳书,约瑟夫Albahari
在C#中线程化 – 免费电子书
ManualResetEvent是AutoResetEvent的变体。 它的区别在于,线程在WaitOne调用之后不会自动重置,所以函数就像一个门:调用Set打开门,允许WaitOne在门口通过任意数量的线程; 调用重置会closures门,这可能导致等待队列的积累,直到下一次打开为止。
人们可以用一个布尔型的“gateOpen”字段(用volatile关键字声明)和“spin-sleeping”一起模拟这个function – 重复检查标志,然后在短时间内hibernate。
ManualResetEvents有时用于表示特定操作已完成,或者线程已完成初始化并准备好执行工作。
我创build了简单的示例来阐明对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(); } } }
恩,那就对了。
你可以通过使用这两个来获得一个想法。
如果你需要告诉你已经完成了一些工作,而其他正在等待的工作(线程)现在可以继续,那么你应该使用ManualResetEvent。
如果您需要对任何资源具有互斥访问权限,则应使用AutoResetEvent。
autoResetEvent.WaitOne()
类似于
try { manualResetEvent.WaitOne(); } finally { manualResetEvent.Reset(); }
作为primefaces操作
是。 这是绝对正确的。
您可以看到ManualResetEvent作为指示状态的一种方式。 有东西在打开(Set)或closures(Reset)。 发生一段时间。 任何等待该状态发生的线程都可以继续。
AutoResetEvent更像一个信号。 一个镜头显示事情已经发生。 没有任何持续时间的事件。 通常但不一定是发生的“事情”很小,需要由单个线程来处理 – 因此,在单个线程消耗事件之后自动重置。
AutoResetEvent在内存中维护一个布尔variables。 如果布尔variables为false,则阻塞该线程,如果布尔variables为true,则解除阻塞线程。
当我们实例化一个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"); }
我们通过传递2秒作为参数来调用WaitOne方法。 在while循环中,等待信号2秒钟,然后继续工作。 当线程得到信号时,WaitOne返回true并退出循环并打印“Thread got signal”。
设置方法
AutoResetEvent Set方法发送信号给等待线程继续工作。 以下是调用Set方法的语法。
autoResetEvent.Set();
ManualResetEvent在内存中维护一个布尔variables。 当布尔variables为false时,它将阻塞所有线程,当布尔variables为true时,它将取消阻塞所有线程。
当我们实例化一个ManualResetEvent时,我们用默认的布尔值初始化它。
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
在上面的代码中,我们使用false值初始化ManualResetEvent,这意味着所有调用WaitOne方法的线程都会阻塞,直到某个线程调用Set()方法。
如果我们用真正的值初始化ManualResetEvent,所有调用WaitOne方法的线程都不会被阻塞,并自由地继续前进。
WaitOne方法
这个方法阻塞当前线程,并等待其他线程的信号。 它返回true,如果它接收到一个信号,否则返回false。
以下是调用WaitOne方法的语法。
manualResetEvent.WaitOne();
在WaitOne方法的第二个重载中,我们可以指定直到当前线程等待信号的时间间隔。 如果内部时间内,它不会收到一个信号,它返回false,并进入方法的下一行。
下面是以时间间隔调用WaitOne方法的语法。
bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
我们已经指定了WaitOne方法的5秒钟。 如果manualResetEvent对象在5秒之间未收到信号,则将isSignalledvariables设置为false。
设置方法
该方法用于将信号发送到所有等待的线程。 Set()方法将ManualResetEvent对象布尔variables设置为true。 所有等待的线程都被解锁并继续进行。
以下是调用Set()方法的语法。
manualResetEvent.Set();
重置方法
一旦我们调用ManualResetEvent对象上的Set()方法,其布尔值仍然为true。 要重置值,我们可以使用Reset()方法。 Reset方法将布尔值更改为false。
以下是调用Reset方法的语法。
manualResetEvent.Reset();
如果要多次向线程发送信号,我们必须在调用Set方法后马上调用Reset方法。
好吧,通常在同一个线程中添加2个答案并不是一个好习惯,但我不想编辑/删除我以前的答案,因为它可以以另一种方式提供帮助。
现在,我创build了更全面,更易于理解的,下面的运行学习控制台应用程序片段。
只需在两个不同的控制台上运行示例,并观察行为。 你会更清楚地知道幕后发生了什么。
手动重置事件
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); } } }