Parallel.ForEach
просто не будет агрессивно освобождать потоки из пула потоков. Планировщик Task определит, сколько потоков вы должны использовать, и в зависимости от ядер, что ваша система уже делает, и эвристики, которую она извлекает из ваших задач, она может не делать то, что вы ожидаете ,
Тем не менее, у вас есть большие концептуальные проблемы здесь. Вы используете Parallel.ForEach
(для того, что выглядит) хлеб-и-масло IO-связанную работу , которая связывает / блокирует потоки, которые могут быть более подходящим образом выгружены в IO Completion ports , и, в свою очередь, имеет больше шансов для достижения более одновременных IO Bound рабочих нагрузок .
Короче говоря, Parallel.ForEach
просто не подходит для рабочих нагрузок, связанных с вводом-выводом , он оптимизирован для рабочих нагрузок, связанных с ЦП и не поддерживает asyn c и ждите паттерна .
Таким образом, вы должны указать, что ваши методы IO будут asyn c (полностью вниз) и с использованием более подходящего технология, которая может работать с asyn c и шаблоном ожидания , например Task.WhenAll
или TPL DataFlow ActionBlock<T>
или Reactive Extensions . Кроме того, вам, вероятно, следует объединить вашу работу с базой данных , так что вы тоже не будете ее уничтожать. Это даст вам максимальную вероятность того, что ваши рабочие нагрузки не будут блокировать потоки пул потоков, и, в свою очередь, вы, скорее всего, найдете сквозное ( в общем) будет намного выше.