我使用的是VS 2012,.Net 4.5。
执行以下代码(只需升级关于线程的文章中的一些示例):
using System.Threading;
class BasicWaitHandle
{
static EventWaitHandle wh = new AutoResetEvent(false);
static void Main()
{
new Thread(Waiter).Start();
new Thread(Waiter).Start();
Thread.Sleep(1000); // Подождать некоторое время...
wh.Set(); // OK – можно разбудить
wh.Set();
Console.ReadLine();
}
static void Waiter()
{
Console.WriteLine("Avait..."+Thread.CurrentThread.ManagedThreadId);
wh.WaitOne(); // Ожидать сигнала
Console.WriteLine("Got a signal"+Thread.CurrentThread.ManagedThreadId);
}
}
我调试了它几次,但通常(并不总是)得到错误的结果。起初(一次或多次)它是正确的:
Avait...10
Avait...11
Got a signal 11
Got a signal 10
但随后它就开始跳过一个线程(有时是第一个?有时是第二个):
Avait...10
Avait...11
Got a signal 11 (or 10)
程序就是并没有反应。几分钟后,它给出了一些正确的结果,但随后又出了问题。。。
此外,当我逐步调试它时,它总是正确运行。
所以,也许我应该选择另一种方法?但这看起来像我所期望的,即使线程以随机顺序获得信号。。。
我很不确定您是否可以对多个唤醒程序使用相同的AutoResetEvent
,因为Set
没有等待第一个线程完成其Wait
:
不能保证每次对Set方法的调用都会从重置模式为EventResetMode.AutoReset的EventWaitHandle中释放一个线程。如果两个调用靠得太近,以至于第二个调用发生在线程释放之前,则只释放一个螺纹。就好像第二次通话没有发生一样。此外,如果在没有线程等待的情况下调用Set,并且EventWaitHandle已发出信号,则该调用无效。
我会在设置信号时使用ManualResetEvent
和同步(以确保等待线程接收信号),或者(更好)为每个等待函数使用专用事件(每个线程都会从自己的事件开始等待,你需要为这些线程创建一种管理器来创建等待事件,并使用Set
方法来通知所有这些事件).
p.s.:可以用俄语btw^^重复上述内容
两个线程都启动并运行,直到它们在WaitHandle上阻塞为止。当设置了WaitHandle时,一个线程将被唤醒,事件将重置。
您无法保证哪个线程会唤醒,因此无法确保顺序。当正确运行时,10或11每次都会醒来,然后是另一个。
在应用程序挂起的情况下,问题在于执行顺序。在第一个线程唤醒之前,主线程正在执行对Event.Set()的两个调用。AutoResetEvent不是计数器,它是已设置或未设置的,因此对set()的第二次调用将丢失。
如果您在对Set()的调用之间Sleep(),您将屈服于其他线程,并给其中一个线程时间来唤醒和重置事件。
在它正常工作的情况下,你只是运气好,等待的线程有机会在对Set()的调用之间运行。这被称为竞争状况。