Не полный ответ, но вот что я узнал до сих пор:
Parallel.ForEach не ведет себя как традиционный многопоточный подход, который не будет иметь проблем с обменом данными, как это делает пример кода.Даже при защите общих данных для предотвращения одновременного доступа, по-видимому, происходит оптимизация в логике ForEach, которая не работает должным образом, если параллельные потоки должны быть заблокированы (например, обработка объектов в последовательном порядке).
Чтобы получить небольшую картину странности, которая происходит с Parallel.ForEach, попробуйте запустить этот фрагмент:
static void Run()
{
System.Collections.Generic.IEnumerable<int> getWorkItems()
{
int workCount = 9999;
while (workCount > 0)
{
System.Console.Write($"R");
yield return workCount--;
Thread.Sleep(10);
}
}
System.Threading.Tasks.Parallel.ForEach(
getWorkItems(),
(workItem) => System.Console.Write($"."));
}
output:
R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.
RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR
..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..RR..
RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR..
..RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR
....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RR
RR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....RRRR....
RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR......
..RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR....
....RRRRRRRR........RRRRRRRR........R.RRRRRRRR........RRRRRRRR........RRRRRRRR
........RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRR
RR........RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRR
RRRR........R.RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR........
RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR......
..RRRRRRRR........RRRRRRRR........RRRRRRRR........RRRRRRRR........R.RRRRRRRR..
......RRRRRRRRRRRR...
...и так далее.У меня нет объяснения этому поведению, но я предполагаю, что обработчик интегратора пытается оптимизировать объем буфера ввода.В любом случае такое поведение приводит к хаосу в коде, который пытается синхронизироваться с общими объектами.
Мораль истории: используйте параллель. Если вы выполняете чисто параллельную обработку и не требует синхронизации.Если вам нужна синхронизация, попробуйте другой подход, такой как предварительная сборка данных в массив или написание собственного многопоточного обработчика.