Вам действительно не нужно так много потоков.Диск может поддерживать только максимальную пропускную способность чтения и записи, которую может легко максимизировать один поток, если он предназначен для ввода-вывода, то есть для чтения или записи.Вы также не можете одновременно читать и записывать на жесткий диск (хотя это сложно с уровнями кэширования ОС и т. Д.), Поэтому одновременное чтение и запись потоков может быть очень непродуктивным.Также мало что можно получить от наличия большего количества потоков, чем процессоров \ ядер для ваших задач, не связанных с вводом-выводом, поскольку любые дополнительные потоки будут тратить большую часть своего времени на ожидание доступности ядра, например, если у вас 50 потоков и 4 ядра, минимумиз 46 потоков будут простаивать в любой момент времени.Потраченные впустую потоки будут способствовать как расходу памяти, так и снижению производительности, поскольку они все время будут бороться за трещину в ядре, и ОС вынуждена решать эту битву.
Более простой подход будетиметь один поток, чье задание читать в файлах, а затем добавлять данные в очередь блокировки (например, см. ConcurrentQueue ), в то же время иметь ряд рабочих потоков, ожидающих данных файла вочередь (например, число потоков, равное количеству процессоров \ ядер).Эти рабочие потоки пробираются через очередь при добавлении элементов и блокируются, когда она пуста.Когда рабочий поток завершает часть работы, он может добавить ее в другую очередь блокировки, которая отслеживается либо потоком чтения, либо выделенным потоком записи.Его работа заключается в том, чтобы записывать файлы.
Этот шаблон стремится сбалансировать ввод-вывод и ЦП среди гораздо меньшего количества взаимодействующих потоков, где количество потоков ввода-вывода ограничено тем, что физически возможно жесткимдиск, и количество рабочих потоков процессора, которое разумно для числа процессоров \ ядер, которые у вас есть.По сути, он разделяет работу ввода-вывода и процессора, так что все ведет себя более предсказуемо.
В дополнение к этому, если IO действительно является проблемой (а не огромным количеством потоков, все сражаются друг с другом), то вы можете сделать несколько пауз (например, Thread.Sleep) в своем файле, читая и записывая потоки вограничить, сколько работы они делают.
Обновление
Возможно, стоит объяснить, почему в первую очередь генерируется так много потоков.Это дегенеративный случай использования пула потоков, и он сосредоточен вокруг очередей рабочих элементов, в которых есть компонент ввода-вывода.
Пул потоков выполняет рабочие элементы из своей очереди и отслеживает, сколько времени занимает выполнение рабочих элементов.Если выполняющиеся в настоящее время рабочие элементы занимают много времени (я думаю, что полсекунды из памяти), то он начнет добавлять больше потоков в пул, поскольку полагает, что это сделает обработку очереди более справедливой \ более справедливой.Однако если дополнительные параллельные рабочие элементы также выполняют операции ввода-вывода с общим диском, производительность диска фактически снижается, а это означает, что выполнение рабочих элементов займет еще больше времени.Поскольку рабочие элементы выполняются дольше, пул потоков добавляет больше потоков.Это дегенеративный случай, когда производительность становится все хуже и хуже по мере добавления большего количества потоков.
Использование семафора, как предлагается, должно быть сделано осторожно, так как семафор может вызвать блокировку потоков пула потоков, пула потоковувидит, что выполнение рабочих элементов займет много времени, и он все равно начнет добавлять больше потоков.