Эффективная многопоточность

  • Вид работы:
    Реферат
  • Предмет:
    Информатика, ВТ, телекоммуникации
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    29,41 kb
  • Опубликовано:
    2009-01-12
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Эффективная многопоточность

Эффективная многопоточность

Алексей Ширшов

Введение

Итак, снова многопоточность. Вы скажете, какая избитая тема, уж сколько можно про это писать! Да, написано про нее немало. Практически каждый программист, который с нею сталкивался (то есть хоть раз в жизни вызвал функцию CreateThread), может заявить, что он про нее знает все или почти все. Но это глубокое заблуждение. Создание эффективных многопоточных серверов (а делать многопоточного клиента особого смысла нет) – дело сложное и требующее хороших знаний системных механизмов: многопоточности, синхронизации, асинхронного ввода/вывода и много другого. В этой статье я коснусь темы организации пула потоков для эффективной обработки клиентских запросов.

Зачем это нужно

Зачем организовывать пул потоков? Вопрос очень широко распространен, и на него давно существует ответ. Для тех, кто этот ответ знает, данный раздел не будет чем-то новым, так что можете его пропускать.

При приходе клиентского запроса у сервера имеется несколько вариантов действий:

Обрабатывать все запросы в одном потоке;

Обрабатывать каждый запрос в отдельном потоке;

Организовать пул потоков.

Рассмотрим каждый из сценариев.

Обработка всех запросов в одном потоке

Сразу понятно, что это решение подходит только для очень ограниченного числа случаев, в которых количество клиентов невелико, и обращаются они к серверу не часто. Эта самая простая схема работы: минимум потоков, минимум ресурсов, и не нужно ничего синхронизировать. Главное, что нужно сделать – построить очередь входящих запросов, чтобы они не терялись при последовательной обработке. Это несложно, к тому же можно взять уже готовые решения: например, СОМ-сервер STA singleton.

Обработка каждого запроса в отдельном потоке

Это, пожалуй, самая популярная у разработчиков схема. В ней для каждого клиентского запроса создается отдельный поток. Решение это простое и для многих случаев удовлетворительное, так как при этом нужно заботиться, по большому счету, только о синхронизации общих переменных потоков (а их может и не быть). Такая схема работает следующим образом: первичный поток приложения прослушивает клиентские запросы и при поступлении каждого создает новый поток, передавая ему клиентский пакет (данные или команду). Созданный поток выполняет соответствующую обработку, передает результаты обратно клиенту, или же помещает их в БД (или еще куда-нибудь), и завершает свое существование.

Давайте задумаемся, что произойдет, если клиентов окажется слишком много. Сервер для каждого из них будет создавать поток, а это, с точки зрения системы, непростая операция, требующая определенного времени и ресурсов. Виртуальное адресное пространство процесса также уменьшается как минимум на принятый по умолчанию для потока размер стека. Все это очень плохо. Сервер тратит время и ресурсы на создание потока, который обрабатывает клиентский запрос всего за доли секунды и затем уничтожается. При этом мы должны учитывать, что физически одновременно выполняться могут только количество потоков, не превышающее числа процессоров на компьютере. На ОС Windows NT/2000 при 100 одновременно запущенных потоках наш сервер будет работать очень неоптимально, что отрицательно скажется на времени обработки запроса.

Основные недостатки такой модели:

частое создание и завершение потоков;

малое время работы потока;

нерегулируемое количество потоков;

в большинстве случаев отсутствие очереди клиентских запросов;

большое количество переключений контекстов рабочих потоков.

Для решения этих проблем и предназначен пул потоков.

Организация пула потоков

Что такое пул потоков? В жизни мы очень часто встречаемся с организацией пула. Например, когда вы идете в столовую, вы встречаетесь с пулом подносов. Да-да, не смейтесь. Подносы организованы в пул (попробуйте объяснить это поварам :) ); клиентов может быть намного меньше, чем подносов, и наоборот. Когда подносов много, они лежат без дела, когда подносов мало, клиенты ждут, пока они освободятся. Число подносов, то есть размер пула, заранее определяется так, чтобы в большинстве случаев клиенты не ждали подносов. Однако случаются часы пик, когда клиентов очень много. Просто нереально выделить отдельный поднос каждому клиенту, да и не нужно это. Клиент все равно будет стоять в очереди к кассе, так что траты на подносы не принесут реальных выгод. Это, конечно, очень далекая и несовершенная аналогия, но она показывает, что в природе и жизни пул чего-либо очень часто используется как наиэффективнейшая схема обслуживания запросов.

Рассмотрим механизм работы пула потоков. Имеется главный поток приложения, прослушивающий клиентские запросы. Пул потоков создается заранее или при поступлении первого запроса. Минимальный размер пула обычно выбирается равным 1, однако это непринципиально. При поступлении запроса главный поток выбирает поток из пула и передает ему запрос. Если количество потоков в пуле достигло максимума, запрос помещается в очередь. Если количество потоков меньше максимального, и все они заняты обработкой, создается новый поток, который получает клиентский пакет на обработку. Если количество потоков равно максимальному и все потоки занимаются обработкой, то есть активны, пакет ставится в очередь и ждет освобождения одного из потоков. Алгоритмы добавления потоков в пул и определения оптимального размера пула сильно зависят от решаемой задачи. Более подробно об этом будет сказано позже.

При использовании RPC-транспорта (в случае с СОМ-серверами) о пуле потоков заботиться не нужно. СОМ-сервер MTA singleton – лучшее решение для СОМ в том смысле, что ничего не нужно делать по поводу организации пула потоков. Система (точнее СОМ-runtime) все делает сама. Однако, если вы используете чистый RPC, вам придется все организовывать самому.

Примитивы операционной системы

Сразу оговорюсь, что в качестве операционной системы я буду рассматривать Windows NT версии 3.1 и выше. Для функций, которые появились позже, версия ОС будет оговариваться отдельно. Линейка Windows 9x не предоставляет никаких средств для организации пула потоков.

В операционной системе есть три механизма организации очереди запросов (очередь запросов – неотъемлемая часть пула): DPC – deferred procedure call (отложенный вызов процедуры), APC – asynchronous procedure call (асинхронный вызов процедуры) и объект ядра queue (очередь), которая доступна приложениям пользовательского режима (user mode) в виде более сложного объекта "порт завершения ввода/вывода". DPC используется только в режиме ядра (kernel mode) в основном драйверами устройств для более эффективной обработки запросов ввода/вывода. DPC мы рассматривать не будем, так как эта тема больше касается программирования драйверов устройств, а мы собираемся писать прикладную программу пользовательского режима. APC, в отличии от DPC, всегда выполняется в контексте какого либо потока (с каждым потоком ассоциирована своя очередь APC-запросов) и может генерировать страничные ошибки (page faults), ожидать перехода объекта ядра в сигнальное состояние, и так далее.

ПРИМЕЧАНИЕ

А почему функции DPC не могут генерировать страничные ошибки? Дело в том, что DPC и APC ставятся в очередь системой с помощью программного прерывания и обрабатываются на определенном уровне прерываний IRQL – interrupt request level. IRQL DPC совпадает с IRQL dispatch, на котором обрабатываются страничные ошибки (он даже называется DPC/dispatch, чтобы отразить это). Как только система поднимает текущий уровень до DPC/dispatch, все прерывания с меньшим или равным уровнем маскируются (блокируются). После обработки DPC система понижает уровень и, если в очереди находиться еще один DPC-запрос, вновь генерируется программное прерывание. Если при обработке DPC-запроса случится обращение к странице памяти, не находящейся в физической памяти, система не сможет «подкачать» эту страницу с диска. Уровень прерывания IRQL APC ниже DPC/dispatch, так что APC могут свободно наслаждаться всеми прелестями виртуального адресного пространства процесса.


Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!