И еще кое-что о таймерах
Таймеры часто применяются в коммуникационных протоколах. Например, ссли кли ент делает запрос серверу и тот не отвечает в течение определенного времени, кли ент считает, что сервер не доступен. Сегодня клиентские машины взаимодействуют, как правило, со множеством серверов одновременно. Если бы объект ядра «таймер» создавался для каждого запроса, производительность системы снизилась бы весьма заметно. В большинстве приложений можно создавать единственный объект-таймер и по мере необходимости просто изменять время его срабатывания.
Постоянное отслеживание параметров таймера и его перенастройка довольно утомительны, из-за чего реализованы лишь в немногих приложениях. Однако в чис ле новых функций для операций с пулами потоков (о них — в главе 11) появилась CreateTimerQueueTimer — она как раз и берет на себя всю эту рутинную работу. При смотритесь к ней, если в Вашей программе приходится создавать несколько объек тов-таймеров и управлять ими.
Конечно, очень мило, что таймеры поддерживают АРС-очереди, но большинство современных приложений использует не APC, а порты завершения ввода-вывода. Как то раз мне понадобилось, чтобы один из потоков в пуле (управляемом через порт завершения ввода-вывода) пробуждался по таймеру через определенные интервалы времени К сожалению, такую функциональность ожидаемые таймеры yе поддержи вают. Для решения этой задачи мнс пришлось создать отдельный поток, который все го-то и делал, что настраивал ожидаемый таймер и ждал его освобождения Когда таймер переходил в свободное состояние, этот поток вызывал PostQueuedComplction Status, передавая соответствующее уведомление потоку в пуле.
Любой, мало-мальски опытный Windows-программист непременно поинтересу ется различиями ожидаемых таймеров и таймеров User (настраиваемых через функ цию SetTimer). Так вот, главное отличие в том, что ожидаемые таймеры реализованы в ядре, а значит, не столь тяжеловесны, как таймеры User. Кроме того, это означает, что ожидаемые таймеры — объекты защищенные.
Таймеры User генерируют сообщения WM_TIMER, посылаемые тому потоку, кото рый вызвал SetTimer (в случае таймеров с обратной связью) или создал определенное
окно (в случае оконных таймеров). Таким образом, о срабатывании таймера User уве домляется только один поток А ожидаемый таймер позволяет ждять любому числу потоков, и, если это таймер со сбросом вручную, при его освобождении может про буждаться сразу несколько потоков.
Если в ответ на срабатывание таймера Вы собираетесь выполнять какие-то опе рации, связанные с пользовательским интерфейсом, то, по-видимому, будет легче структурировать код под таймеры User, поскольку применение ожидаемых таймеров требует от потоков ожидания не только сообщений, но и объектов ядра (Если у Вас есть желание переделать свой код, используйте функцию MsgWaitForMultipleObjects, которая как раз и рассчитана на такие ситуации.) Наконец, в случае ожидаемых тай меров Вы с большей вероятностью будете получать уведомления именно no истече нии заданного интервала. Как поясняется в главе 26, сообщения WM_TIMER всегда имеют наименьший приоритет и принимаются, только когда в очереди потока нет других сообщений Но ожидаемый таймср обрабатывястся так же, как и любой дру гой объект ядра, если он сработал, ждущий поток немедленно пробуждается