Изучение средств синхронизации процессов и потоков в ОС Windows: Методические рекомендации по выполнению лабораторной работы, страница 2

 События подразделяются на мануальные (manualevents) и единичные (singleevents). Мануальное событие – это такое, флаг которого любой поток может установить с помощью вызова SetEvent и сбросить с помощью ResetEvent. Вызов PulseEvent приведет к тому, что все потоки, ожидающие этого события, получат сигнал о том, что событие произошло, и только после этого событие сбрасывается. Т.е. после PulseEvent все потоки, ожидающие этого события, получают сообщения, что событие произошло.  Если тип события – единичное, то после того, как событие произойдет, только один из ожидающих потоков получит информацию об этом и сможет продолжить  работу. Когда несколько потоков ожидают некоторое событие, то лучше использовать мануальное событие.

  Для системных вызовов, предназначенных для работы с событиями, служат функции API Windows, приведенные в таблице 1.

Синтаксис функции CreateEvent:

HANDLE  CreateEvent (

LPSECURITY_ATTRIBUTES  lpEventAttributes, // указатель на атрибуты безопасности

BOOL  bManualReset,  // флаг вида события

BOOL  bInitialState,     //   флаг состояния инициализации

LPCTSTR  lpName      //   указатель на имя события

).

            Первый параметр – указатель на структуру SECURITY_ATTRIBUTES, которая содержит атрибуты безопасности создаваемого объекта.  Если параметр имеет значение NULL, то используются атрибуты по умолчанию.

Таблица  1

Функция

Назначение

CreateEvent

OpenEvent

SetEvent

ResetEvent

PulseEvent

Создает событие или открывает существующее событие

Открывает существующее событие

Устанавливает событие

Сбрасывает событие

Переводит событие в сигнальное состояние, после того как на событие прореагируют все ожидающие потоки, затем событие сбрасывается

            Параметр bManualReset определяет, будет ли созданное событие  переустанавливаться автоматически или вручную (мануально). Если он имеет значение TRUE, то для переустановки состояния на несигнальное следует использовать функцию ResetEvent. При значении FALSE  тип события – единичное.

            Следующий параметр bInitialState устанавливает начальное состояние события. Если его значение TRUE, то начальное состояние – сигнальное и наоборот.

            Аргумент lpName представляет собой указатель на имя события, для события без имени его значение NULL.

            Функция CreateEvent() при успешном завершении возвращает хэндл события, при ошибке с помощью GetLastError можно получить код ошибки.

            Функция SetEvent() устанавливает состояние события как сигнальное.  Её синтаксис:

BOOLSetEvent(HANDLE   hEvent // дескриптор события, возвращаемый функцией CreateEvent или OpenEvent.

            Функция  BOOLResetEvent(HANDLE hEvent) переустанавливает (сбрасывает)  состояние события на несигнальное.

            Программа может узнать о наступлении события с помощью функции WaitForSingleObject(), описанной ранее.

            В Приложении 1 приведен пример программы [2], иллюстрирующей использование единичного события. Если эту программу запустить без аргументов (argc = = 1), то вызовом WaitForSingleObject()  она переходит в режим ожидания события, которое создается функцией CreateEvent(). Если после этого ещё раз запустить программу с любым аргументом (argc = =2), то программа с помощью функции SetEvent() устанавливает событие в сигнальное состояние. На это событие прореагирует ожидающая событие предыдущая копия программы. Эта копия выходит из ожидания события и после закрытия окна сообщения  завершает свою работу.

            Можно запустить несколько копий программы без аргументов, а затем – программу с аргументом. При каждом запуске программы с аргументом завершает работу только одна из запущенных ранее копий программы, так как создается единичное событие.

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

2.3.   Мьютексы   (mutexs)

Мьютекс обеспечивает возможность исключительного (exclusive) доступа к ресурсу, который  им защищается. Например, если несколько потоков (процессов) должны работать с одним и тем же связным списком, то мьютекс может позволить доступ к списку на время выполнения операции, например, добавление, удаление элемента, только одному из потоков. Мьютексы способны запоминать, какому потоку они принадлежат.

Поток сохраняет право на владение мьютексом до тех пор, пока он не вызовет функцию   ReleaseMutex. Если поток захватил мьютекс, а потом завершился без освобождения мьютекса, то ОС подстраховывает и при завершении потока переводит мьютекс в свободное состояние.

Для создания нового мьютекса следует вызвать функцию API  CreateMutex(). Её синтаксис:

HANDLE    CreateMutex(LPSECURITY_ATTRIBUTES  lpMutexsAttributes,   BOOL   bInitialOwner,   LPCTSTR   lpName).

Первый параметр lpMutexsAttributes – указатель на структуру атрибутов безопасности создаваемого мьютекса,  параметр bInitialOwner определяет начальную принадлежность мьютекса.  Если он имеет значение TRUE, то вызывающий поток изначально является владельцем мьютекса, если – FALSE, то не является. Последний параметр lpName представляет собой указатель на строку – имя мьютекса, при значении параметра  NULL создается мьютекс без имени.

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

Функция OpenMutex() возвращает идентификатор уже существующего мьютекса. Синтаксис функции

HANDLE    OpenMutex(

   DWORD       dwDesiredAccess,  // флаг доступа к мьютексу

   BOOL            bInheritHandle,     // флаг  наследования

   LPCTSTR     lpName              // указатель на имя мьютекса

).

Параметр dwDesiredAccess определяет доступ к индексу, если этот параметр принимает значение MUTEX_ALL_ACCESS, то допустимы все виды доступа. Значение SYNCHRONIZE используется только для Windows NT и разрешает использование хэндла мьютекса в функции WaitForSingleObject. Параметр bInheritHandle может принимать значения TRUE или FALSE. В первом случае процесс, созданный функцией CreateProcess, может наследовать хэндл мьютекса, во втором – это не длпускается. Наконец, последний параметр lpName указывает нак строку с именем мьютекса.