Создал простую программу с использованием Linqpad, где я явно выделяю исключение в цикле Parallel Foreach
, который идеально должен быть пойман в вызывающем агенте как Aggregate Exception
, но когда я явно бросаю исключение, он иногда пропускает несколько исключений на случайной основе. Я не могу понять поведение, кто может объяснить:
void Main()
{
try
{
var intList = new List<int> {1,2,3,4,5,6};
Parallel.ForEach(intList, i => Test1(i));
}
catch (AggregateException aggregateException)
{
foreach (var ex in aggregateException.Flatten().InnerExceptions)
{
ex.Message.Dump();
}
}
}
public void Test1(int i)
{
try
{
if (i % 2 != 0)
throw new Exception($"{i} - Odd value exception");
}
catch(Exception ex)
{
ex.Message.Dump();
throw;
}
}
public void Test2(int i)
{
if (i % 2 != 0)
throw new Exception($"{i} - Odd value exception");
}
public void Test3(int i)
{
try
{
if (i % 2 != 0)
throw new Exception($"{i} - Odd value exception");
}
catch(Exception ex)
{
ex.Message.Dump();
}
}
Test3
которая всегда работает как исключение, явно не выбрасывается из параллельного циклаDump
- вызов печати linqpad, замените его на Console.WriteLine
на визуальной студии Там вариант определить здесь, который собирает все исключения в виде ConcurrentQueue
и бросить их позже как агрегированный исключения, но почему текущий код не работает, как ожидалось, я не очень уверен. В этом случае мы ожидаем, что Output будет:
1 - Odd value exception
3 - Odd value exception
5 - Odd value exception
но некоторые из них случайным образом пропущены, что тоже в простой программе, в сложной программе гораздо больше пропусков, которые делают гораздо больше работы
Это вполне ожидаемое поведение.
См. Документы,
необработанное исключение приводит к немедленному завершению цикла
Когда вы создаете исключение, новые задания не будут назначены.
Таким образом, поведение будет непредсказуемым. Вы не имеете права ожидать, что все подзадачи будут выполнены. Это не контракт цикла Parallel.For.
Разница будет намного яснее, если вы добавите больше элементов в исходный список. На выходе всегда будет отображаться ряд исключений в окрестности ThreadPool.MinThreads.