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

2016-07-31СтудИзба

Описание файла

Документ из архива "Эффективная многопоточность", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "рефераты, доклады и презентации", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "45817"

Текст из документа "45817"

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

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

Введение

Итак, снова многопоточность. Вы скажете, какая избитая тема, уж сколько можно про это писать! Да, написано про нее немало. Практически каждый программист, который с нею сталкивался (то есть хоть раз в жизни вызвал функцию 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 могут свободно наслаждаться всеми прелестями виртуального адресного пространства процесса.

APC бывают двух видов: режима ядра и пользовательского режима. APC режима ядра отличается от APC пользовательского режима тем, что система может прервать работу потока для вызова процедуры без его ведома, тогда как для исполнения APC пользовательского режима поток должен находится в специальном «тревожном» (alertable) ожидании, как бы давая согласие на исполнение процедуры. Объект "очередь" и его производный объект "порт завершения ввода/вывода" специально предназначены для организации пула и, кроме очереди запросов, могут управлять ассоциированными с ними потоками. Давайте рассмотрим APC пользовательского режима и порт завершения ввода/вывода более подробно.

APC пользовательского режима

Этот механизм можно использовать, если нужно выполнить какую-либо операцию (функцию) в контексте определенного потока. Для выполнения функции поток должен «дать согласие», перейдя в состояние тревожного ожидания (alertable wait state). Если поток находится в таком состоянии, то, как только мы поставим в очередь APC-запрос с указанием адреса функции и произвольного параметра для нее, поток перейдет к выполнению данной функции, после чего выйдет из состояния ожидания. APC пользовательского режима могут использовать функции ReadFileEx, WriteFileEx, а также SetWaitableTimer, о которой мы поговорим отдельно. Функции ReadFileEx и WriteFileEx предназначены специально для асинхронных операций – для них вы обязаны открывать файл (файл в самом общем смысле) в асинхронном режиме, указывая флаг FILE_FLAG_OVERLAPPED, а также для каждой операции создавать структуру OVERLAPPED. В качестве последнего параметра обе функции принимают адрес специальной функции завершения – FileIOCompletionRoutine. После завершения асинхронной операции, если поток находится в тревожном ожидании, эта функция будет вызвана с помощью механизма APC. В тревожное ожидание поток может перейти с помощью «расширенных» функций ожидания, которые оканчиваются на Ex. Это SleepEx, WaitForSingleObjectEx, WaitForMultipleObjectsEx и другие. Для того чтобы вручную поместить APC-запрос в очередь потока, нужно воспользоваться функцией QueueUserAPC. Вот ее прототип:

DWORD QueueUserAPC(

PAPCFUNC pfnAPC, // APC функция

HANDLE hThread, // хендл потока

ULONG_PTR dwData // параметр APC функции

);

Рассмотрим небольшой пример ее использования (проверка ошибок устранена для повышения наглядности).

const int _SOME_MAGIC_VALUE = 5;

DWORD CALLBACK trd1(LPVOID p)

{

HANDLE hEvent = (HANDLE)p;

SetEvent(hEvent);

int i = 0;

while(i < _SOME_MAGIC_VALUE){

SleepEx(INFINITE, true);

cout << i++ << endl;

}

return 0;

}

VOID CALLBACK APCProc(ULONG_PTR dwParam)

{

cout << "APC Proc #" << dwParam;

cout << " threadid :" << GetCurrentThreadId() << endl;

}

int main()

{

HANDLE hEvent = CreateEvent(0, false, false, NULL);

DWORD trd_id = 0;

HANDLE hThread = CreateThread(0, 0, trd1, hEvent, 0, &trd_id);

cout << "Thread id is 0x" << hex << trd_id << endl;

WaitForSingleObject(hEvent, INFINITE);

for(int i = 0;i < _SOME_MAGIC_VALUE;i++){

QueueUserAPC(APCProc, hThread, i);

}

WaitForSingleObject(hThread, 1000);

CloseHandle(hThread);

return 0;

}

Несмотря на кажущуюся простоту, пример довольно сложен, и не всегда можно предсказать, что будет на экране после его завершения. Давайте разберем его в форме вопрос-ответ.

Почему я синхронизирую потоки с помощью события?

Если этого не сделать, то все пять APC-запросов выполнятся еще до того, как функция потока trd1 получит управление. Это произойдет потому, что сама система в процессе создания потока использует механизм APC-вызовов для инициализации потока. С его помощью, например, происходит вызов всех функций DllMain с параметром DLL_THREAD_ATTACH, если, конечно, вы не вызывали DisableThreadLibraryCalls для какой-либо библиотеки.

Почему на экран выводится странный результат?

Thread id is 0x68c

APC Proc #0 threadid :68c

0

APC Proc #1 threadid :68c

APC Proc #2 threadid :68c

APC Proc #3 threadid :68c

APC Proc #4 threadid :68c

1

Как я уже говорил, для каждого потока система организует очередь из APC-запросов, так что в момент обработки первого запроса потоком система успевает добавить в очередь все остальные запросы, которые и выполняются при следующем вызове SleepEx. Результат сильно зависит от загруженности системы, так что у вас он может быть другим: например, все запросы успеют выполниться за одну итерацию.

Свежие статьи
Популярно сейчас
Зачем заказывать выполнение своего задания, если оно уже было выполнено много много раз? Его можно просто купить или даже скачать бесплатно на СтудИзбе. Найдите нужный учебный материал у нас!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Нашёл ошибку?
Или хочешь предложить что-то улучшить на этой странице? Напиши об этом и получи бонус!
Бонус рассчитывается индивидуально в каждом случае и может быть в виде баллов или бесплатной услуги от студизбы.
Предложить исправление
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5120
Авторов
на СтудИзбе
444
Средний доход
с одного платного файла
Обучение Подробнее