Синхронизация событий - PullRequest
3 голосов
/ 09 июня 2011

Я заметил, что иногда мой код не синхронизируется, если событие срабатывает слишком быстро. Мне было интересно, если есть лучший подход. При обычном сценарии DeviceOpenedEvent срабатывает после того, как я сообщаю потоку WaitOne в методе TestDevice, но я видел в некоторых случаях, когда событие вызывается до того, как поток имеет шанс подождать.

    protected AutoResetEvent TestAutoResetEvent = new AutoResetEvent(false);
    public EventEnum WaitForEvent = EventEnum.None;

    bool TestDevice()
    {
        OpenDevice();

        WaitForEvent = EventEnum.DeviceOpened;
        TestAutoResetEvent.WaitOne();
        WaitForEvent = EventEnum.NoWait;

        //Continue with other tests
    }

    void DeviceOpenedEvent()
    {
        if (WaitForEvent == EventEnum.DeviceOpened)         
            TestAutoResetEvent.Set();                           
    }

В обычных условиях это выглядит так:

  1. Открытое устройство
  2. WaitOne ()
  3. DeviceOpenedEvent происходит
  4. Set ()

Это то, что я иногда вижу в своих журналах:

  1. Открытое устройство
  2. DeviceOpenedEvent происходит
  3. WaitOne () По существу застрял здесь навсегда

Ответы [ 2 ]

2 голосов
/ 09 июня 2011

Поскольку OpenDevice является асинхронным (как вы упомянули в комментарии), он работает в потоке, отличном от вызывающего. Иногда это заканчивается до того, как будет выполнена следующая строка в источнике:

    OpenDevice(); // Async: may finish before the next line executes!
    WaitForEvent = EventEnum.DeviceOpened;

Когда это происходит, DeviceOpenedEvent не делает то, что вы хотите, потому что WaitForEvent по-прежнему EventEnum.None:

if (WaitForEvent == EventEnum.DeviceOpened)         
    TestAutoResetEvent.Set(); 

Решение состоит в том, чтобы изменить ваш код так, чтобы вы сигнализировали о завершении внутри метода, который гарантированно работает в правильном порядке. Вот простая реализация, которая удаляет перечисление и использует один дескриптор ожидания для каждого события, которое вам нужно ждать:

protected AutoResetEvent deviceOpenedEvent = new AutoResetEvent(false);
protected AutoResetEvent deviceLockedEvent = new AutoResetEvent(false);

bool TestDevice() {
    OpenDevice();
    // Do some unrelated parallel stuff here ... then
    deviceOpenedEvent.WaitOne();
    LockDevice();
    deviceLockedEvent.WaitOne();
}

void DeviceOpenedEvent() {
    deviceOpenedEvent.Set();                           
}

Еще проще, если вы контролируете OpenDevice: просто позвоните deviceOpened.Set(), когда все будет готово. Вы даже можете изменить OpenDevice, чтобы принять событие автоматического сброса и построить его прямо внутри TestDevice, что уменьшит вашу подверженность многопоточным ошибкам.

1 голос
/ 09 июня 2011

Это не должно быть проблемой.Документация для AutoResetEvent гласит:

Если поток вызывает WaitOne, когда AutoResetEvent находится в сигнальном состоянии, поток не блокируется.

Следующий код не приводит к блокировке WaitOne, например:

AutoResetEvent waitHandle = new AutoResetEvent(false);
waitHandle.Set();
waitHandle.WaitOne();
Console.WriteLine("After WaitOne");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...