Это условие гонки в вашем коде, а не ошибка в RhinoMocks.Проблема возникает при настройке списка задач allTasks в методе Start()
:
public void Start()
{
var allTasks = new List<Task>();
foreach (var foo in _fooList)
// the next line has a bug
allTasks.Add(Task.Factory.StartNew(() => foo.DoSomething()));
Task.WaitAll(allTasks.ToArray());
}
Вам необходимо явно передать экземпляр foo в задачу.Задача будет выполняться в другом потоке, и вполне вероятно, что цикл foreach заменит значение foo до ее запуска.
Это означает, что каждый foo.DoSomething()
вызывается иногда никогда, а иногда больше, чемодин раз.По этой причине некоторые задачи будут блокироваться на неопределенный срок, потому что RhinoMocks не может обработать перекрывающийся вызов событий на одном и том же экземпляре из разных потоков и попадает в тупик.
Замените эту строку в вашем методе Start
:
allTasks.Add(Task.Factory.StartNew(() => foo.DoSomething()));
С этим:
allTasks.Add(Task.Factory.StartNew(f => ((IFoo)f).DoSomething(), foo));
Это классическая ошибка, которая является тонкой и очень легко игнорируется.Иногда его называют «доступ к измененному замыканию».
PS:
После комментариев к этому посту я переписал этот тест с использованием Moq.В этом случае он не блокируется - но имейте в виду, что ожидания, созданные для данного экземпляра, могут быть не удовлетворены, если исходная ошибка не будет исправлена, как описано.GenerateFoo () с использованием Moq выглядит следующим образом:
private List<IFoo> GenerateFooList(int max)
{
var fooList = new List<IFoo>();
for (int i = 0; i < max; i++)
fooList.Add(GenerateFoo());
return fooList;
}
private IFoo GenerateFoo()
{
var foo = new Mock<IFoo>();
foo.Setup(f => f.DoSomething()).Raises(f => f.myEvent += null, EventArgs.Empty);
return foo.Object;
}
Он более элегантен, чем RhinoMocks, и явно более терпим к нескольким потокам, вызывающим события в одном и том же экземпляре одновременно.Хотя я не думаю, что это является общим требованием - лично я не часто нахожу сценарии, в которых можно предположить, что подписчики на событие поточнобезопасны.