Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
51
Добавлен:
15.04.2021
Размер:
110.09 Кб
Скачать

Ожидание освобождения нескольких объектов может быть организовано с помощью функции:

DWORD WaitForMultipleObjects(IN DWORD nCount, IN CONST HANDLE *lpHandles, IN BOOL bWaitAll, IN DWORD dwMilliseconds);

где lpHandles – указатель на массив дескрипторов объектов, nCount – число дескрипторов в этом массиве, а параметр bWaitAll определяет логику ожидания. При bWaitAll = true функция ждёт, пока все объекты окажутся в сигнальном состоянии. При bWaitAll = false функция завершает свою работу, если свободен хотя бы один объект. В этом случае возвращаемое функцией значение указывает, какой именно объект находится в этом состоянии.

Функция отличается от функции WaitForSingleObject возвращаемыми значениями. При bWaitAll = true функция возвращает значения WAIT_OBJECT_0, WAIT_TIMEOUT, WAIT_ABANDONED_0 или WAIT_FAILED. При bWaitAll = false возможные значения WAIT_TIMEOUT, WAIT_FAILED, от WAIT_OBJECT_0 до WAIT_OBJECT_0 + nCount 1 и от WAIT_ABANDONED_0 до WAIT_ABANDONED_0 + nCount 1. Разность возвращённого результата WAIT_OBJECT_0 или возвращённого результата WAIT_ABANDONED_0 дают индекс того объекта в массиве lpHandles, который вызвал завершение выполнения функции.

§3. Синхронизация задач с помощью объектов ядра «события».

События являются самыми примитивными объектами ядра, по сути простыми уведомлениями об окончании каких-либо действий. Событие содержит счетчик числа пользователей и две логические переменные: тип события и состояние (свободен или занят).

Различают два типа событий:

события с ручным сбросом;

события с автоматическим сбросом.

Событие с ручным сбросом можно перевести в несигнальное состояние только вызовом функции ResetEvent. Событие с автоматическим сбросом переводится в несигнальное состояние как с помощью функции ResetEvent, так и с помощью функции ожидания, то есть когда поток успешно дождался такого события, он автоматически сбрасывается в несигнальное (занятое) состояние.

Примечание. Если события с автоматическим сбросом ожидают несколько потоков с помощью функции WaitForSingleObject, то из состояния ожидания освобождается только один из этих потоков.

Событие создается функцией

HANDLE CreateEvent(IN LPSECURITY_ATTRIBUTES lpEventAttributes, IN BOOL bManualReset, IN BOOL bInitialState, IN LPCSTR lpName);

где

lpEventAttributes – указатель наследования возвращенного дескриптора дочерними процессами и указатель используемого дескриптора защиты; по умолчанию NULL (дескриптор не наследуется, используется дескриптор защиты по умолчанию);

bManualReset определяет тип: true – с ручным сбросом, false – с автоматическим.

bInitialState задает начальное состояние: true – сигнальное, false – несигнальное.

1

lpName – это имя события, которое позволяет обращаться к событию из потоков разных процессов. Имя может быть не задано, тогда создается событие без имени. Событие без имени можно использовать только в рамках одного процесса.

При успешном выполнении функция CreateEvent возвращает дескриптор события, иначе NULL. Узнать после вызова CreateEvent, существовало ли уже такое событие, можно оператором:

if(GetLastError() == ERROR_ALREADY_EXISTS)

Функции BOOL SetEvent(HANDLE hEvent) и BOOL ResetEvent(HANDLE hEvent); устанавливают событие в сигнальное и несигнальное состояния соответственно.

Управлять состоянием события можно также функцией BOOL PulseEvent(HANDLE hEvent), которая эквивалентна последовательному выполнению функций SetEvent и ResetEvent. В случае события с ручным сбросом это позволит всем потокам, которые ждут событие и могут немедленно завершить ожидание (например ждут только это событие), выйти из ожидания. Затем функция PulseEvent переводит событие в несигнальное состояние и завершает работу. Для событий с автоматическим сбросом функция позволит одному из потоков выйти из ожидания (если это возможно), после чего переводит событие в несигнальное состояние и завершает работу.

Функция HANDLE OpenEvent(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName) открывает существующий объект события с именем lpName и возвращает его дескриптор.

Параметр dwDesiredAccess может принимать значения:

EVENT_ALL_ACCESS: поток может выполнять любые действия над событием;

EVENT_MODIFY_STATE: поток может использовать функции SetEvent и ResetEvent;

SYNCHRONIZE: поток может использовать событие в функциях ожидания. Параметр bInheritHandle определяет, наследуется ли возвращаемый дескриптор

дочерними процессами. Значение true означает наследование.

Функция OpenEvent возвращает дескриптор только в случае, если объект события уже был создан каким-то процессом. В противном случае возвращается NULL.

Пример. Поток 1 готовит данные для обработки независимыми (параллельно работающими) потоками 2 и 3. В потоке 1 размещаем код:

HANDLE E[2];

E[0] = CreateEvent(NULL, true, false, "MyEvent2");

E[1] = CreateEvent(NULL, true, false, "MyEvent3");

HANDLE H = CreateEvent(NULL, true, false, "MyEvent1");

//подготовка данных

...

//установка события MyEvent1 SetEvent(H);

//ожидание событий MyEvent2 и MyEvent 3 WaitForMultipleObjects(2, E, true, INFINITE); ResetEvent(E[0]);

ResetEvent(E[1]);

ResetEvent(H);

2

// продолжение выполнения после установки событий MyEvent2 и MyEvent3

С помощью события "MyEvent1" поток 1 будет оповещать потоки 2 и 3 об окончании подготовки данных. События "MyEvent2" и "MyEvent3" будут использоваться для получения информации от потоков 2 и 3. После завершения подготовки данных событие "MyEvent1" с помощью функции SetEvent переводится в сигнальное состояние, которое будет опознано потоками 2 и 3. Затем вызывается функция WaitForMultipleObjects, обеспечивающая ожидание до тех пор, пока оба события "MyEvent2" и "MyEvent3" не окажутся в сигнальном состоянии. После этого работа потока 1 будет продолжена. Перед продолжением работы все события сбрасываются в несигнальное состояние функцией ResetEvent. Такой сброс необходим, если приведённый код выполняется больше одного раза. Поскольку окончание ожидания наступает при сигнальном состоянии событий "MyEvent2" и "MyEvent3", то оно сохранится, и при повторном обращении к функции WaitForMultipleObjects ожидание не состоится. В потоке 2, который должен среагировать на событие "MyEvent1", выполнить свои операции над подготовленными данными и оповестить о завершении выполнения поток 1 через событие "MyEvent2", размещаем код:

HANDLE H = CreateEvent(NULL, true, false, "MyEvent1")

WaitForSingleObject(H, INFINITE);

//начало обработки данных, подготовленных потоком 1

...

//сообщение об окончании обработки SetEvent(OpenEvent(EVENT_ALL_ACCESS, true, "MyEvent2"));

Код начинается с создания дескриптора Н события "MyEvent1" и ожидания того, когда это событие будет переведено первым приложением в сигнальное состояние. Когда это происходит, начинается обработка полученных данных. После этого функцией OpenEvent открывается дескриптор события "MyEvent2" и функцией SetEvent событие переводится в сигнальное состояние.

В потоке 3, который должен среагировать на событие "MyEvent1", выполнить свои операции над подготовленными данными и оповестить о завершении выполнения поток 1 через событие "MyEvent3", размещаем аналогичный код, заменив в нем имя события "MyEvent2" на MyEvent3".

3

Соседние файлы в папке АОПИ. Лекции