EventWaitHandle иногда! пропустить тему

Я использую 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)

И программа просто не реагирует. Через несколько минут это дает некоторые правильные результаты, но затем снова идет не так...

Более того, когда я отлаживаю его шаг за шагом, он всегда действует правильно.

Итак, может быть, я должен выбрать другой подход? Но это похоже на то, что я ожидал, даже если потоки получали сигналы в случайном порядке...

2 ответа

Решение

Я совершенно не уверен, что вы можете использовать то же самое AutoResetEvent для нескольких человек, потому что Set не ждет первого потока, чтобы завершить его Wait:

Нет никакой гарантии, что каждый вызов метода Set освободит поток из EventWaitHandle, чей режим сброса - EventResetMode.AutoReset. Если два вызова расположены слишком близко друг к другу, так что второй вызов происходит до освобождения потока, освобождается только один поток. Как будто второго звонка не произошло. Кроме того, если Set вызывается, когда нет ожидающих потоков и EventWaitHandle уже сигнализирован, вызов не имеет никакого эффекта.

Я бы пошел с ManualResetEvent и синхронизация во время установки сигнала (чтобы гарантировать, какой ожидающий поток получает сигнал) или (лучше) использовать выделенное событие для каждой ожидающей функции (каждый поток будет начинаться со своего собственного события, которое нужно ждать, вам понадобится своего рода менеджер для создания этого потока ожидание события и иметь Set метод, который будет сигнализировать все эти события).

PS: можно повторить сказанное выше на русском языке кстати ^^

Оба потока запускаются и работают до тех пор, пока не блокируются в WaitHandle. Когда WaitHandle установлен, один поток проснется и событие будет сброшено.

Вы не можете гарантировать, какая нить проснется, поэтому порядок не гарантирован. При правильной работе 10 или 11 будут просыпаться, а затем каждый раз другой.

В случае зависания вашего приложения проблема заключается в порядке выполнения. Основной поток выполняет оба вызова Event.Set() до пробуждения первого потока. AutoResetEvent не является счетчиком, он либо установлен, либо не установлен, поэтому второй вызов Set () теряется.

Если вы спите () между вызовами Set(), вы уступите другим потокам и дадите одному из них время для пробуждения и сброса события.

В случае, если это работает правильно, вам просто повезло, и ожидающие потоки получают возможность запускаться между вызовами Set(). Это упоминается как состояние гонки.

Другие вопросы по тегам