Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows


Сценарий 1: асинхронный вызов функций


Допустим, у Вас есть серверный процесс с основным потоком, который ждет клиен тский запрос. Получив его, он порождает отдельный поток для обработки этого зап роса Тем самым основной поток освобождается для приема следующего клиентского запроса. Такой сценарий типичен в клиент-серверных приложениях. Хотя он и так то незагейлив, при желании его можно реализовать с использованием новых функ ций пула потоков.

Получая клиентский запрос, основной поток вызывает:

BOOL QueueUserWorkItem( PTHREAD_START_ROUTINE pfnCallback, PVOID pvContext, ULONG dwFlags);

Эта функция помещает «рабочий элемент" (work item) в очсрсдь потока в пулс и тут же возвращает управление. Рабочий элемент — это просто вызов функции (па которую ссылается параметр pfnCallback), принимающей единственный параметр, pvContext. В конечном счете какой-то поток из пула займется обработкой этого эле

мента, в результате чего будет вызвана Ваша функция. У этой функции обратного вызова, за реализацию которой отвечаете Вы, должен быть следующий прототип:

DWORD WINAPI WorkItemFunc(PVOID pvContext);

Несмотря па то что тип возвращаемого значения определен как DWORD, на са мом деле оно игнорируется.

Обратите внимание, что Вы сами никогда не вызываете CreateThread Она вызы вается из пула потоков, автоматически создаваемого для Вашего процесса, а к функ ции WorkItemFunc обращается один из потоков этого пула. Кроме того, данный поток не уничтожается сразу после обработки клиентского запроса, а возвращается в пул, оставаясь готовым к обработке любых других элементов, помещаемых в очередь. Ваше приложение может стать гораздо эффективнее, так как Вам больше не придется со здавать и уничтожать потоки для каждого клиентского запроса. А поскольку потоки связаны с определенным портом завершения, количество одновременно работающих потоков не может превышать число процессоров более чем в 2 раза. За счет этого переключения контекста происходят реже.

Многое в пуле потоков происходит скрытно от разработчика: QueueUserWorkItem проверяет число потоков, включенных в сферу ответственности компонента поддер жки других операций (нс относящихся к вводу-выводу), и в зависимости от текущей нагрузки (количества рабочих элементов в очереди) может передать емудругие по токи. После этого QueueUserWorkltem выполняет операции, эквивалентные вызову PostQueuedCompletionStatus, пересылая информацию о рабочем элементе в порт за вершения ввода-вывода. В конечном счете поток, ждущий на этом объекте, извлекает Ваше сообщение (вызовом GetQueuedCompletionStatus} и обращается к Вашей функ ции. После того как она возвращает управление, поток вновь вызывает GetQueued ComplettonStatus, ожидая появления следующего рабочего элемента




Начало    Вперед