Часть полного текста документа:Эффективная многопоточность Алексей Ширшов Введение Итак, снова многопоточность. Вы скажете, какая избитая тема, уж сколько можно про это писать! Да, написано про нее немало. Практически каждый программист, который с нею сталкивался (то есть хоть раз в жизни вызвал функцию CreateThread), может заявить, что он про нее знает все или почти все. Но это глубокое заблуждение. Создание эффективных многопоточных серверов (а делать многопоточного клиента особого смысла нет) - дело сложное и требующее хороших знаний системных механизмов: многопоточности, синхронизации, асинхронного ввода/вывода и много другого. В этой статье я коснусь темы организации пула потоков для эффективной обработки клиентских запросов. Зачем это нужно Зачем организовывать пул потоков? Вопрос очень широко распространен, и на него давно существует ответ. Для тех, кто этот ответ знает, данный раздел не будет чем-то новым, так что можете его пропускать. При приходе клиентского запроса у сервера имеется несколько вариантов действий: Обрабатывать все запросы в одном потоке; Обрабатывать каждый запрос в отдельном потоке; Организовать пул потоков. Рассмотрим каждый из сценариев. Обработка всех запросов в одном потоке Сразу понятно, что это решение подходит только для очень ограниченного числа случаев, в которых количество клиентов невелико, и обращаются они к серверу не часто. Эта самая простая схема работы: минимум потоков, минимум ресурсов, и не нужно ничего синхронизировать. Главное, что нужно сделать - построить очередь входящих запросов, чтобы они не терялись при последовательной обработке. Это несложно, к тому же можно взять уже готовые решения: например, СОМ-сервер STA singleton. Обработка каждого запроса в отдельном потоке Это, пожалуй, самая популярная у разработчиков схема. В ней для каждого клиентского запроса создается отдельный поток. Решение это простое и для многих случаев удовлетворительное, так как при этом нужно заботиться, по большому счету, только о синхронизации общих переменных потоков (а их может и не быть). Такая схема работает следующим образом: первичный поток приложения прослушивает клиентские запросы и при поступлении каждого создает новый поток, передавая ему клиентский пакет (данные или команду). Созданный поток выполняет соответствующую обработку, передает результаты обратно клиенту, или же помещает их в БД (или еще куда-нибудь), и завершает свое существование. Давайте задумаемся, что произойдет, если клиентов окажется слишком много. Сервер для каждого из них будет создавать поток, а это, с точки зрения системы, непростая операция, требующая определенного времени и ресурсов. Виртуальное адресное пространство процесса также уменьшается как минимум на принятый по умолчанию для потока размер стека. Все это очень плохо. Сервер тратит время и ресурсы на создание потока, который обрабатывает клиентский запрос всего за доли секунды и затем уничтожается. При этом мы должны учитывать, что физически одновременно выполняться могут только количество потоков, не превышающее числа процессоров на компьютере. На ОС Windows NT/2000 при 100 одновременно запущенных потоках наш сервер будет работать очень неоптимально, что отрицательно скажется на времени обработки запроса. Основные недостатки такой модели: частое создание и завершение потоков; малое время работы потока; нерегулируемое количество потоков; в большинстве случаев отсутствие очереди клиентских запросов; большое количество переключений контекстов рабочих потоков. ............ |