О потоках CLR
Часто при проектировании и разработке высокопроизводительных систем, разработчики рассматривают многопоточность как один из способов ускорения работы приложения, а также эффективного распределения нагрузки.
В тоже время многие забывают, что потоки в CLR привязаны к возможностям операционной системы, а следовательно (как мы увидим ниже) имеют достаточно ограниченые возможности. Каждый поток ОС, как известно, требует определенные ресурсы системы, и, в частности, память. Сюда следует также прибавить накладные расходы на управление потоками со стороны ядра ОС.
Вот таким незамысловатым кодом можно проверить “возможности” операционной системы в текущей аппаратной конфигурации.
class Program
{
static void Main(string[] args)
{
int i = 0;
try
{
while (true)
{
new Thread(new ThreadStart(() => Thread.Sleep(int.MaxValue))).Start();
i++;
}
}
catch (Exception ex)
{
Console.WriteLine(i);
Console.WriteLine(ex.ToString());
}
}
}
После определенного количества времени ожидания получаем вот такое исключение:
Показания монитора ресурсов подтверждает полученные цифры:
На моей машине с процессором Core 2 Duo, 4ГБ оперативной памяти и ОС Windows Server 2008 R2 x64, в среднем создается около 15-ти тысяч потоков.
Не будем здесь подробно останавливаться о природе таких ограничений (любопытным можно рекомендовать книги Джеффри Рихтера и ресурсы MSDN), отметим лишь один очень важный вывод: использование модели потоков (threads) в операционной системе Windows, а следовательно и в CLR, в виде 1 поток на 1 запрос не является оптимальным решением при проектировании высокопроизводительных систем. Наиболее распространенную альтернативу – пул потоков (Thread Pool) и его реализацию в CLR рассмотрим в ближайшее время.