В пуле потоков есть алгоритм восхождения на холм, который он использует для оценки соответствующего количества потоков. Пока добавление потоков увеличивает пропускную способность, пул потоков будет создавать больше потоков. Он будет предполагать, что происходит некоторая блокировка или ввод-вывод, и пытается насыщать ЦП, пропуская количество процессоров в системе.
Именно поэтому выполнение операций ввода-вывода и блокировка чего-либо в потоках пула потоков может быть опасным.
Вот полностью рабочий пример такого поведения:
BlockingCollection<string> _streamingData = new BlockingCollection<string>();
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 100; i++)
{
_streamingData.Add(i.ToString());
Thread.Sleep(100);
}
});
new Thread(() =>
{
while (true)
{
Thread.Sleep(1000);
Console.WriteLine("Thread count: " + Process.GetCurrentProcess().Threads.Count);
}
}).Start();
Parallel.ForEach(_streamingData.GetConsumingEnumerable(), item =>
{
});
Я не знаю, почему количество потоков продолжает расти, хотя и не увеличивает пропускную способность. Согласно модели, которую я объяснил, она не будет расти. Но я не знаю, правильна ли моя модель.
Возможно, у пула потоков есть дополнительная эвристика, которая делает его порождающим потоки, если он вообще не видит прогресса (измеряется в задачах, выполняемых в секунду). Это имело бы смысл, потому что это, вероятно, предотвратило бы много взаимоблокировок в приложениях. Блокировки могут возникнуть, если важные задачи не могут быть запущены, поскольку они ожидают выхода существующих задач и обеспечения доступности потоков. Это хорошо известная проблема с пулом потоков.