1

我正在使用 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)

而程序只是没有反应。几分钟后它给出了一些正确的结果,但又出错了......

此外,当我逐步调试它时,它总是正确运行。

那么,也许我应该选择另一种方法?但这看起来像我预期的那样,即使线程以随机顺序收到信号......

4

2 回答 2

1

I am pretty unsure you can use same AutoResetEvent for multiple awaters, because Set is not waiting for first thread to complete its Wait:

There is no guarantee that every call to the Set method will release a thread from an EventWaitHandle whose reset mode is EventResetMode.AutoReset. If two calls are too close together, so that the second call occurs before a thread has been released, only one thread is released. It is as if the second call did not happen. Also, if Set is called when there are no threads waiting and the EventWaitHandle is already signaled, the call has no effect.

I'd go with ManualResetEvent and synchronization during setting signal (to ensure, what waiting thread receive signal) or (better) use dedicated event for each waiting function (every thread would start with its own event to wait for, you will need kind of manager for those thread to create waiting event and to have Set method what will signal all these events).

p.s.: can repeat said above in russian btw ^^

于 2014-01-13T15:19:01.200 回答
0

两个线程都启动并运行,直到它们在 WaitHandle 上阻塞。设置 WaitHandle 后,将唤醒一个线程并重置事件。

你不能保证哪个线程会被唤醒,所以不能保证顺序。正确运行时,每次都会唤醒 10 或 11,然后再唤醒另一个。

在您的应用程序挂起的情况下,问题在于执行顺序。在第一个线程唤醒之前,主线程正在执行对 Event.Set() 的两个调用。AutoResetEvent 不是计数器,它要么已设置要么未设置,因此对 Set() 的第二次调用丢失。

如果在 Set() 调用之间使用 Sleep(),您将让步给其他线程,并给其中一个线程唤醒和重置事件的时间。

在它正常工作的情况下,你很幸运,等待的线程有机会在对 Set() 的调用之间运行。这被称为竞争条件。

于 2014-01-13T15:23:10.373 回答