Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
348
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

Interlocked-функции (пользовательского режима) никогда не приводят к исключе нию потока из числа планируемых; они лишь изменяют какое-то значение и тут же возвращают управление.

Другие функции, применяемые в синхронизации потоков

При синхронизации потоков чащс всего используются функции WaitForSingleObject и WaitForMultipleObjects. OднaкoвWindows ecть и дpyгиe, нecкoлькo oтличaющиecя фyнк ции, которые можно применять с той же целью. Если Вы понимаете, как работают Wait ForSingleObject и WaitForMultipleQbjects, Вы без труда разберетесь и в этих функциях.

Асинхронный ввод-вывод на устройствах

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

Объекты устройств являютсн синхронизируемыми объектами ядра, а это означа ет, что Вы можете вызывать WaitForSingleObject и передавать ей описатель какого-либо файла, сокета, коммуникационного порта и т. д. Пока система выполняет асинхрон ный вводвывод, объект устройства пребывает в занятом состоянии. Как только опе рация заканчивается, система переводит объект в свободное состояние, и поток уз нает о завершении операции. С этого момента поток возобновляет выполнение.

Функция WaitForlnputldle

Поток может приостановить себя и вызовом WaitForlnputIdle:

DWORD WaitForInputIdle( HANDLE hProcess, DWORD dwMilliseconds);

Эта функция ждет, пока у процесса, идентифицируемого описателем bProcess, не опустеет очередь ввода в потоке, создавшем первое окно приложения. WaitForlnputldle полезна для применения, например, в родительском процессе, который порождает дочерний для выполнения какой-либо нужной ему работы. Когда один из потоков родительского процесса вызывает CreateProcess, он продолжает выполнение и в то время, пока дочерний процесс инициализируется. Этому потоку может понадобить ся описатель окна, создаваемого дочерним процессом. Единственная возможность узнать о моменте окончания инициализации дочернего процесса — дождаться, когда тот прекратит обработку любого ввода Поэтому после вызова CreateProcess поток родительского процесса должен вызвать WaitForInputIdle.

Эту функцию можно применить и в том случае, когда Вы хотите имитировать в программе нажатие каких-либо клавиш. Допустим, Вы асинхронно отправили в глав ное окно приложения следующие сообщения:

WM_KEYDOWN с виртуальной клавишей VK_MENU

WM_KEYDOWN с виртуальной клавишей VK_F

WM_KEYUP с вирчуальной клавишей VK_F

WM_KEYUP с виртуальной клавишей VK_MENU

WM_KEYDOWN с виртуальной клавишей VK_O

WM_KEYUP с виртуальной клавишей VK_O

Эта последовательность дает тот же эффект, что и нажатие клавиш Alt+F, О, — в большинстве англоязычных приложений это вызывает команду Open из меню File. Выбор данной команды открывает диалоговое окно; но, прежде чем оно появится на экране, Windows должна загрузить шаблон диалогового окна из файла и «пройтись» по всем элементам управления в шаблоне, вызывая для каждого из них функцию CreateWindow. Разумеется, на это уходит какое-то время. Поэтому приложение, асин хронно отправившее сообщения типа WM_KEY*, теперь может вызвать WaitForlnput ldle и таким образом перейти в режим ожидания до того момента, как Windows за кончит создание диалогового окна и оно будет готово к приему данных от пользова теля. Далее программа может передать диалоговому окну и сго элементам управле ния сообщения о еще каких-то клавишах, что заставит диалоговое окно проделать те илииныеоперации.

С этой проблемой, кстати, сталкивались многие разработчики приложений для 16 разрядной Windows Программам нужно было асинхронно передавать сообщения в окно, но получить точной информации о том, создано ли это окно и готово ли к работе, они не могли. Функция WaitForlnputldle решает эту проблему

Функция MsgWaitForMultipleObjects(Ex)

При вызове MsgWaitForMultipleObjects или MsgWaitForMultipleObjectsEx поток переходит в ожидание своих (предназначенных этому потоку) сообщений:

DWORD MsgWaitForMultipleObjects( DWORD dwCount, PHANDLE phObjects, BOOL fWaitAll, DWORD dwMilliseconds, DWORD dwWakeMask);

DWORD MsgWaitForMultipleObjectsEx( DWORD dwCount, PHANDLE phObjects, DWORD dwMillisGConds, DWORD dwWakeMask DWORD dwFlags);

Эти функции аналогичны WaitForMultipleObjects. Единственное различие заключа ется в том, что они пробуждают поток, когда освобождается некий объект ядра или когда определенное оконное сообщение требует перенаправления в окно, созданное вызывающим потоком.

Поток, который создает окна и выполняетдругие операции, относящиеся к пользо вательскому интерфейсу, должен работать с функцией MsgWaitForMultipleObjectsEx, а не с WaitForMultipleObjects, так как последняя не дает возможности реагировать на действия пользователя Подробнее эти функции рассматриваются в главе 26

Функция WaitForDebugEvent

В Windows встроены богатейшие отладочные средства Начиная исполнение, отлад чик подключает себя к отлаживаемой программе, а потом просто ждет, когда опера ционная система уведомит его о каком-нибудь событии отладки, связанном с этой программой Ожидание таких событий осуществляется через вызов

BOOL WaitForDebugEvent( PDEBLIG_F_VENT pde, DWORD dwMimseconds);

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

Функция SignalObjectAndWait

SignalObjectAndWait переводит в свободное состояние один объект ядра и ждет дру гой объеют ядра, выполняя все это как одну операцию на уровне атомарного доступа:

DWORD SignalObjectAndWait( HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds, BOOL fAlertable);

Параметр hObjectToSignal должен идентифицировать мьютекс, семафор или собы тие; объекты любого другого типа заставят SignalObjectAndWait вернуть WAIT_FAILED, а

функцию GetLastError — ERROR_INVALIDHANDLE. Функция SignalObjectAndWait про веряет тип объекта и выполняет действия, аналогичные тем, которые предпринима ют функции ReleaseMutex, ReleaseSemaphore (со счетчиком, равным 1) или ResetEvent.

Параметр hObjectToWaitOn идентифицирует любой из следующих объектов ядра: мьютекс, семафор, событие, таймер, процесс, поток, задание, уведомление об изме нении файла или консольный ввод. Параметр dwMilliseconds, как обычно, определяет, сколько времени функция будет ждать освобождения объекта, a флаг fAlertable указы вает, сможет ли поток в процессе ожидания обрабатывать посылаемые ему АРС-вы зовы.

Функция возвращает одно из следующих значений: WAIT_OBJECT_0, WAIT_TIME OUT, WAIT_FAILED, WATT_ABANDONED (см. раздел о мьютексах) или WAIT_IO_COMP

LETION.

SignalObjectAndWait — удачное добавление к Windows AFI по двум причинам. Bo псрвых, освобождение одного объекта и ожидание другого — задача весьма распро страненная, а значит, объединение двух операций в одной функции экономит про цессорное время. Каждый вызов функции, заставляющей поток переходить из кода, который работает в пользовательском режиме, в код, работающий в режиме ядра, требует примерно 1000 процессорных тактов (на платформах x86), и поэтому для выполнения, например, такого кода:

ReleaseMutex(hMutex); WaitForSingleObject{hEvent, INFINITE);

понадобится около 2000 тактов. В высокопроизводительных серверных приложени ях SignalObjectAndWait дает заметную экономию процессорного времени.

Во-вторых, без функции SignalObjectAndWait ни у одного потока не было бы воз можности узнать, что другой поток перешел в состояние ожидания. Знание таких вещей очень полеяно для функций типа PulseEvent. Как я уже говорил в этой главе, PulseEvenl переводит событие в свободное состояние и тут же сбрасывает его. Если ни один из потоков не ждет данный объект, событие не зафиксирует этот импульс (pulse). Я встречал программистов, которые пишут вот такой код:

// выполняем какие-то операции