Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
3 семестр, WinAPI, MFC.pdf
Скачиваний:
370
Добавлен:
15.06.2014
Размер:
6.17 Mб
Скачать

свои операции. Чтобы лучше разобраться в этом механизме, вернемся к примеру с

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

Когда объект «событие со сбросом вручную» переходит в свободное состояние, система разрешает исполнение всех ожидавших его потоков. Поток переводит такой объект «событие» в свободное состояние вызовом SetEvent

BOOL SetEvent(HANDLE hEvent)

Эта функция принимает описатель объекта «событие» и меняет состояние объекта на свободное. Если операция прошла успешно, функция возвращает TRUE. После перевода объекта в свободное состояние он пребывает в нем, пока какой-нибудь поток явным образом (т. е «вручную») не сбросит событие, вызвав:

BOOL ResetEvent(HANDLE hEvent)

Эта функция принимает описатель объекта «событие» и переводит объект в занятое состояние. Если операция прошла успешно, функция возвращает TRUE.

Как поток, считывающий данные из файла, узнает, что пора считывать следующий блок? Известно, что это должно произойти по окончании работы прочих потоков с текущей порцией данных. Значит, и эти потоки должны как-то сигнализировать об окончании своих операций. Лучше всего создать в каждом из них собственный объект «событие», а описатели этих объектов поместить в массив; тогда поток, отвечающий за чтение данных, сможет воспользоваться функцией WaitForMultipleObjects и сообщить ей, что он собирается ждать освобождения всех девяти объектов.

Поскольку на практике нередко после вызова SetEvent объект «событие» освобождается и тут же следует вызов ResetEvent, в Win32 предусмотрена дополнительная функция, способная самостоятельно выполнять все три операции:

BOOL PulseEvent(HANDLE hEvent),

После возврата управления функцией PulseEvent событие остается в занятом состоянии. Если функция выполнена успешно, возвращается TRUE.

Ожидаемые таймеры

Это объекты ядра, самостоятельно переходящие в свободное состояние в определенное время и/или через регулярные промежутки времени. Чтобы создать ожидаемый таймер, достаточно вызвать функцию CreateWaitable Timer.

HANDLE CreateWaitableTimer(LPSECURITY_ATTRIBUTES Ipsa, BOOL

DManualReset, LPCTSTR IpszTimerName)

Разумеется, любой процесс может получить свой («процессо-зависимый») описатель существующего объекта «ожидаемый таймер», вызвав OpenWaitableTimer.

HANDLE OpenWaitableTimer(DWORD dwDesiredAccess,

BOOL

blnheritHandle, LPCTSTR IpszTimerName);

 

Получив описатель ожидаемого таймера, процесс может настроить этот таймер вызовом:

BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER «pDueTime, LONG IPeriod,

PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID IpArgToCompletionRoutine, BOOL fResume);

Таймер переходит в сигнальное состояние, когда истекает его «таймаут». Отменить «тиканье таймера» можно функцией CancelWaitableTimer

BOOL CancelWaitableTimer(HANDLE hTimer)

Эта очень простая функция принимает описатель таймера и отменяет его, после чего тот уже никогда не сработает, — если только Вы не переустановите его повторным вызовом SetWaitableTimer. Кстати, если Вам понадобится перенастроить таймер, то вызывать CancelWaitableTimer перед повторным обращением к SetWaitableTimer не требуется; каждый вызов SetWaitableTimer автоматически отменяет предыдущие настройки перед установкой новых.

Примечательно, что можно указать callback-функцию при установке таймера. Она будет выполняться, когда срабатывает таймер.

Любой Windows-программист непременно поинтересуется различиями ожидаемых таймеров и таймеров User (устанавливаемых через функцию SetTimer). Так вот, главное отличие в том, что ожидаемые таймеры реализованы в ядре, а значит,

не столь тяжеловесны, как таймеры User. Кроме того, это означает, что ожидаемые таймеры — объекты защищенные.

Сводная таблица объектов, используемых для синхронизации потоков

В следующей таблице 8 приведены краткие сведения о различных объектах ядра применительно к синхронизации потоков.

Объект

Находится в занятом

Переходит в свободное

Побочный эффект

 

состоянии, когда

состояние, когда

успешного

 

 

 

ожидания

Процесс

процесс еще активен поток

процесс завершается (ExitProcess,

Нет

Поток

еще активен

TerminateProcess)

Нет

 

 

поток завершается (ExitThread,

 

 

 

TerminateThread)

 

Объект

Находится в занятом

Переходит в свободное

Побочный эффект

 

состоянии, когда:

состояние, когда:

успешного

 

 

 

ожидания

Задание

время, выделенное заданию,

время, выделенное заданию,

Нет

 

еще не истекло

истекло

 

Файл

выдан запрос на ввод-вывод

завершено выполнение запроса

Нет

 

 

на ввод-вывод

 

Консольный

ввода нет

ввод есть

Нет

ВВОД

 

 

 

Уведомление об

в файловой системе нет

файловая система обнаруживает

Сбрасывается в

изменении файла

изменений

изменения

исходное

 

 

 

состояние

Событие с

вызывается ResetEvent,

вызывается SetEvent или

Сбрасывается в

автосбросом

PulseEvent или ожидание

PulseEvent

исходное

 

успешно завершилось

 

состояние

Событие со

вызывается ResetEvent или

вызывается SetEvent или

Нет

сбросом вручную

PulseEvent

PulseEvent

 

Ожидаемый

вызывается CancelWaitable-

наступает время срабатывания

Сбрасывается в

таймер с

Тiтеr или ожидание успешно

(SetWaitableTimer)

исходное

автосбросом

завершилось

 

состояние

Ожидаемый

вызывается

наступает время срабатывания

Нет

таймер со сбросом

CancelWaitableTimer

(SetWaitableTimef)

 

вручную

 

 

 

Семафор

ожидание успешно

счетчик > 0 (ReleaseSemaphore)

Счетчик

 

завершилось

 

уменьшается на 1

Мьютекс

ожидание успешно

поток освобождает мьютекс

Передается потоку

 

завершилось

(ReleaseMutex)

во владение

Критическая

ожидание успешно

поток освобождает критическую

Передается потоку

секция

завершилось (

секцию (LeaveCriticalSection)

во владение

(пользовательского

(Try)EnterCriticalSection)

 

 

режима)