AutoResetEvent преждевременно сигнализируется при выполнении модульного теста в Visual Studio 2010 - PullRequest
0 голосов
/ 30 марта 2012

Я запускаю модульный тест MS в VS2010, чтобы протестировать мое многопоточное приложение.

Приложение использует AutoResetEvent для синхронизации потоков, который объявлен так:

private readonly AutoResetEvent captureParsedEvent = new AutoResetEvent(false);

Основная тестовая нить (ID: 13)

Основной тестовый поток запускает поток для анализа файла захвата, а затем вызывает WaitOne() для AutoResetEvent, блокируя до завершения захвата:

int id = Thread.CurrentThread.ManagedThreadId;
CaptureManager.Instance.StartProcessingPackets();
Trace.WriteLine("[" + id + "]: WAITING ON CaptureParsedEvent");
captureParsedEvent.WaitOne();
Trace.WriteLine("[" + id + "]: WAITING ON CaptureParsedEvent DONE!");

// Analyse parsed capture...

( Примечание: в коде изначально был вызов captureParsedEvent.Reset() после WaitOne(), но я удалил это во время исследования этой проблемы, так как мое исследование пришло к выводу, что в этом нет необходимости для AutoResetEvent объектов.)

Нить синтаксического анализа (ID: 18)

Тем временем поток, выполняющий синтаксический анализ, сигнализирует AutoResetEvent следующим образом:

private void InstanceManagerStateChanged(ManagerStateEventArgs ea, object sender)
{
    int id = Thread.CurrentThread.ManagedThreadId;
    switch(ea.CurrentState)
    {
        case ManagerState.ReadPacketsDone:
            Trace.WriteLine("\t[" + id + "]: CaptureParsedEvent SIGNAL");
            captureParsedEvent.Set();
            Trace.WriteLine("\t[" + id + "]: CaptureParsedEvent DONE!");
            break;
    }
}

Обычно все ведет себя хорошо, и я вижу следующий ожидаемый результат в окне вывода:

[13]: WAITING ON CaptureParsedEvent
    [18]: CaptureParsedEvent SIGNAL
    [18]: CaptureParsedEvent DONE!
[13]: WAITING ON CaptureParsedEvent DONE!

Тем не менее, я периодически вижу следующий вывод:

[13]: WAITING ON CaptureParsedEvent
[13]: WAITING ON CaptureParsedEvent DONE!

Это, очевидно, вызывает у меня проблемы, потому что захват действительно не заканчивает анализ.

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

Несколько вопросов:

  1. Является ли Trace.WriteLine() поточно-ориентированным и выводит ли трассы в правильном порядке?

  2. Я видел эту проблему только при запуске модульных тестов в VS2010 - происходит ли что-то смешное с параллельными тестами и использованием потоков в этом сценарии, которые могут вызывать проблемы? Насколько я понимаю, тесты выполняются последовательно, но я не уверен, что это правильно.

Ответы [ 3 ]

1 голос
/ 01 апреля 2012

По умолчанию Visual Stufio 2010 не запускает тесты параллельно, вы должны включить это путем ручного редактирования файла настроек теста (parallelExecutionCount = 0).

При просмотре предоставленного вами кода, виновник может быть связан с тем фактом, что сигнализация выполняется в пределах одного объекта (CaptureManager.Instance). Могут быть ситуации, когда предыдущие тесты уже выполнены, а ManagerState уже завершен. Попробуйте выполнить тест самостоятельно, чтобы подтвердить это предположение.

Если вы выполняете тесты последовательно, вам может потребоваться сбросить состояние одиночного перехода между тестами, чтобы избежать подобных побочных эффектов. Обратите внимание, однако, что это должно быть сделано везде, где используется синглтон, что может оказаться не идеальным, если этот класс интенсивно используется во всей кодовой базе.

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

1 голос
/ 30 марта 2012

Кажется, что то, что вы получаете, вызвано тем фактом, что во время оценки ea.CurrentState не является ManagerState.ReadPacketsDone, поэтому он пропускает код внутри этого оператора case.ManualResetEvent s не устанавливают себя, и если они это сделают, то это будет ОГРОМНАЯ проблема для всех (я никогда не слышал, чтобы кто-то другой имел такую ​​проблему), так что вам просто нужно убедиться, что никто другой не устанавливаетсобытие.

На вопрос 1: Trace.WriteLine() является поточно-ориентированным, но если у вас несколько потоков, делающих вызовы на запись, вы не гарантируете, что эти вызовы будут выполнены по порядку.Однако в вашем случае сообщения SIGNAL и DONE будут записываться одно за другим, поскольку они выполняются в одном и том же потоке.Что еще более важно, если вы получите правильное состояние, то по крайней мере CaptureParsedEvent SIGNAL будет напечатано до WAITING ON CaptureParsedEvent DONE, потому что это происходит до того, как вы установите событие ручного сброса.После подачи сигнала вам не гарантируется порядок отпечатков для WAITING ON CaptureParsedEvent DONE и CaptureParsedEvent DONE.

Если другой поток пишет одновременно, он может записать что-то между ними.Но, как я уже сказал: это, скорее всего, вызвано тем, что ea.CurrentState не является ManagerState.ReadPacketsDone.

На вопрос 2: когда вы имеете дело с параллелизмом, всегда происходит «что-то смешное» или«смешно», как то, что вы обычно получаете при параллельном программировании: вам просто нужно аккуратно .Опять же, я не думаю, что ваша проблема связана с параллелизмом, просто кажется, что вы не обрабатываете правильный случай и / или кто-то другой имеет доступ к тому же ManualResetEvent.

0 голосов
/ 16 апреля 2012

Я решил свою проблему.

Оказывается, что для каждого пакета, который обрабатывал код, он добавлял обратный вызов InstanceManagerStateChanged() в список делегатов:

CaptureManager.Instance.ManagerStateChanged += InstanceManagerStateChanged;

но мы не правильно отписались, что означало, что я мог получать уведомления от предыдущего пакета.

Отмена подписки до обработки следующего пакета устранила эту проблему:

CaptureManager.Instance.ManagerStateChanged -= InstanceManagerStateChanged;
...