2012-02-20 2 views
1

Код в вопросеПочему этот код ThreadPool.QueueUserWorkItem не сбивает мой тест?

public void StartPlaying() 
{ 
    ThreadPool.QueueUserWorkItem(ignoredState => 
    { 
     while (_playlist.Count > 0) 
     { 
      var audioFile = _playlist.Dequeue(); 

      if (StartedPlaying != null) 
       StartedPlaying(this, new TypedAudioFileEventArgs(audioFile)); 

      audioFile.SoundPlayer.PlaySync(); 
      audioFile.SoundPlayer.Dispose(); 

      if (StoppedPlaying != null) 
       StoppedPlaying(this, new TypedAudioFileEventArgs(audioFile)); 
     } 
    }); 
} 

и мой тест:

[TestMethod()] 
public void StartPlayIsCalledTwice_OnlyRunningOnce() 
{ 
    int timeBetweenPlays = 0; 
    var target = new TypedAudioFilePlayer(timeBetweenPlays); 

    target.AddFile(TypedAudioFileCreator.CreateWord(1, "bl")); 
    target.StartedPlaying += StartedPlaying_Once; 

    target.StartPlaying(); 
    target.StartPlaying(); 
} 

private bool _once = false; 
private void StartedPlaying_Once(object sender, TypedAudioFileEventArgs e) 
{ 
    if (!_once) 
     _once = true; 
    else 
     Assert.Fail("Should not be called more than once!"); 
} 

Я считаю, что мой блок тест должен потерпеть неудачу, судя по описанию MSDN из ThreadPool.QueueUserWorkItem:

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

Размер потока ThreadPool по умолчанию равен 512, поэтому для обработки вызова StartPlaying должны быть доступны сразу два потока. Я считаю, что мой код не сработает, поскольку я не предоставил никаких гарантий из условий гонки, в которых оба потока могут получить доступ к одному и тому же ресурсу.

Что здесь происходит?

+0

пытаются объявить _once летучим ... –

+0

@Mitch: Пробовали, тест по-прежнему проходит. Мне нравится, как то, что должно быть технически хорошо (тест прохождения), оказывается источником страха и путаницы (ПОЧЕМУ вы проходите !?). Хех, программные методологии - это весело. – IAE

+0

Даже если в этом коде не было условий гонки, Assert будет поднят в другом потоке в тот, который выполняет тест. – porges

ответ

3

Поскольку событие StartedPlaying возникает только в том случае, если вызывается функция StartPlaying, когда есть предметы для воспроизведения.

_playlist.Dequeue(); удаляет файл, который вы в очереди. Поэтому во второй раз, когда вы дойдете до while (_playlist.Count > 0), он сразу же провалится, пройдя второй звонок до StartPlaying прямо без поднятия события.

Кроме того, как указывает Бруно Силва, нить, порожденная вторым вызовом StartPlaying, может не иметь возможности выполнить что-либо до выхода теста.

Для чего это стоит, есть около миллиона по крайней мере, 2 пронизывающих ошибок в этом коде также:

// Where did _playlist come from? Is it shared state among the player threads? 
// If so, all access to it should be in locks, since queues are not thread safe 
while (_playlist.Count > 0) 


// Both of these start threads and then immediately return. 
// The test will probably exit before either of those threads do anything much 
    target.StartPlaying(); 
    target.StartPlaying(); 
} 

Если вы хотите, чтобы правильно модульное тестирование, необходимо определить предварительные условия, ожидание, действия и постусловия:

  • Предпосылки: у вас есть инициализированная TypedAudioFilePlayer с одним файлом в очереди:

    var target = new TypedAudioFilePlayer(timeBetweenPlays); target.AddFile(TypedAudioFileCreator.CreateWord(1, "bl"));

  • Ожидания: Событие StartedPlaying будет поднят только один раз, если StartPlaying вызывается дважды

    target.StartedPlaying += StartedPlaying_Once;

  • действия: Метод StartPlaying будет называться дважды:

    target.StartPlaying(); target.StartPlaying();

  • Постусловия:StartedPlaying событие только один раз поднял:

    private bool _once = false;

    private void StartedPlaying_Once(object sender, TypedAudioFileEventArgs e) { if (!_once) _once = true; else Assert.Fail("Should not be called more than once!"); }

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

+0

Миллион? Я думал, что у меня просто отсутствует замок, который я еще не написал, ради написания неудачного модульного теста. Это то, о чем мой вопрос, чтобы получить тест в состоянии неустойчивого состояния, прежде чем добавить блокировку. – IAE

+0

Тест не может надежно пройти (или провалиться, в этом случае), пока есть условия гонки. –

+0

Отличная точка зрения, я думаю, что мой подход к модулю тестирования функции с резьбой был явно ошибочным. +1 – IAE

0

Могут ли они не работать вне текста? Ваш тест заканчивается сразу после того, как вы ставите в очередь элемент, поэтому я не уверен, что происходит с этими потоками, когда метод тестирования завершает его выполнение. Вы пробовали использовать WaitHandle, чтобы дождаться, когда они закончатся в тесте?

См., Например, http://msdn.microsoft.com/en-us/library/system.threading.waithandle.aspx.

+0

Что вы имеете в виду после того, как я поставил в очередь предмет? Я дважды вызываю audioPlayer.StartPlaying(), чтобы увидеть, будет ли он дважды запускать событие. Я не останавливаюсь после добавления файла. – IAE

1

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

private readonly object lock_object=new object(); 
private void StartedPlaying_Once(object sender, TypedAudioFileEventArgs e) 
{ 
lock(lock_object) 
{  
if (!_once) 
     _once = true; 
    else 
     Assert.Fail("Should not be called more than once!"); 
} 
} 
+0

Я тоже пробовал это, только я поместил свою блокировку в функцию StartPlaying перед вызовом QueueUserWork (еще одна логическая проверка, если звук уже воспроизводится в другом потоке). Все еще проходит: D – IAE

+0

у вас не получается тест на основе значения _once, поэтому вы должны поместить свою блокировку здесь (общий ресурс :)) – Beatles1692

+0

Благодарим вас за отзыв, надеюсь, если я продолжу заниматься этим, я в конечном итоге неустойчивое состояние ^^ – IAE

Смежные вопросы