Подія (Windows)
Подія (об'єкт події, event object) в операційній системі Windows — об'єкт для синхронізації виконання процесів (потоків), який може знаходитися у двох станах (сигнальному та несигнальному)[1].
Застосунки (прикладні програми) можуть використовувати об'єкти подій у різних ситуаціях, щоб повідомити потік про виникнення потрібної події. В загальному випадку, об’єкти подій використовують для сигналізації очікуючим потокам про досягнення певних умов виконання програми.
Види об'єктів подій
Бувають події ручного скидання (manual-reset) та автоматичного скидання (auto-reset). Подія ручного скидання, будучи сигнальною, залишається такою, доки не буде вимкнена програмно викликом функції API ResetEvent. Події автоматичного скидання перемикаються у несигнальний стан системою, коли один із очікуючих потоків закінчить роботу[1].
Робота з подіями
Для створення події використовується функція API CreateEvent. Її опис мовою C++:[2]:
HANDLE WINAPI CreateEvent( __in_opt LPSECURITY_ATTRIBUTES lpEventAttributes, __in BOOL bManualReset, __in BOOL bInitialState, __in_opt LPCTSTR lpName );
function CreateEvent( lpEventAttributes: PSecurityAttributes; // Атрибути секретності bManualReset, // Задає вид події: ручна (true) чи автоматична (false) bInitialState: BOOL; // Задає початк. стан. Якщо true - сигнальний lpName: PChar // Назва (або nil, якщо не потрібно) ): THandle;
Функція повертає дескриптор створеного об’єкта або нуль у випадку невдачі. Якщо об’єкт події із заданою назвою вже існує, то повертається його дескриптор. При цьому ігноруються параметри bManualReset та bInitialState, а функція GetLastError поверне значення ERROR_ALREADY_EXISTS. Ім’я події не повинно збігатися з іменами існуючих об’єктів типу Semaphore, Mutex, Job, Waitable Timer або File Mapping. Якщо відомо, що подія вже існує, її дескриптор можна отримати функцією OpenEvent. Опис на C++[4]:
HANDLE WINAPI OpenEvent( __in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in LPCTSTR lpName );
function OpenEvent( dwDesiredAccess: DWORD; // Задає права доступу до об'єкта bInheritHandle: BOOL; // Вказує, чи може об'єкт успадковуватися // дочірніми процесами lpName: PChar // Ім'я об'єкта ): THandle;
Функція повертає дескриптор об'єкта або нуль у випадку помилки. Параметр dwDesiredAccess може набувати одне з таких значень:
- EVENT_ALL_ACCESS – програма отримує повний доступ до об'єкта;
- EVENT_MODIFY_STATE – програма може змінювати стан об'єкта функціями SetEvent і ResetEvent;
- SYNCHRONIZE – можна використовувати об'єкт події у функціях синхронізації.
Після отримання дескриптора події його можна використовувати. Для цього Windows API надає такі функції: SetEvent, ResetEvent та PulseEvent. Функція SetEvent встановлює об'єкт у сигнальний стан:
BOOL WINAPI SetEvent(__in HANDLE hEvent); // Опис мовою C++ function SetEvent(hEvent: THandle): BOOL; // Опис мовою Delphi
Функція ResetEvent скидає об'єкт, встановлюючи його в несигнальний стан:
BOOL WINAPI ResetEvent(__in HANDLE hEvent); // Опис мовою C++ function ResetEvent(hEvent: THandle): BOOL; // Опис мовою Delphi
Функція PulseEvent встановлює об'єкт у сигнальний стан, дозволяє відпрацювати всім функціям синхронізації, які його очікують, а потім знову скидає об’єкт події.
BOOL WINAPI PulseEvent(__in HANDLE hEvent); // Опис мовою C++ function PulseEvent(hEvent: THandle): BOOL; // Опис мовою Delphi
Приклад використання об'єкта події
Вказаний приклад демонструє, як за допомогою системного об’єкта події програма може отримувати повідомлення про зміни у певному підрозділі (текст програми мовою Delphi) реєстру Windows[3].
function ThreadFunc(Ptr: Pointer): LongInt; var evnt : THandle; RK : HKey; S : string; begin evnt := CreateEvent(nil, false, false, nil); RegOpenKeyEx(HKEY_CURRENT_USER, 'Software\IC\RegDemo', 0, KEY_READ, RK); RegNotifyChangeKeyValue(RK, true, REG_NOTIFY_CHANGE_LAST_SET, evnt, true); if WaitForSingleObject(evnt, INFINITE) = WAIT_OBJECT_0 then begin S := TimeToStr(Time) + ': У реєстр внесено зміни!'; MessageBox(hWindow, PWideChar(S), 'Ооо!', MB_ICONINFORMATION); end; RegCloseKey(RK); CloseHandle(evnt); end;
Спочатку викликом функції CreateEvent створюється об'єкт події автоматичного скидання, дескриптор якого зберігається у змінній evnt. Після цього відкривається потрібна гілка реєстру та за допомогою функції RegNotifyChangeKeyValue здійснюється підписка на отримання повідомлень про зміни у реєстрі. Таким чином RegNotifyChangeKeyValue пов'язує зміни у реєстрі з об’єктом події evnt. Після цього за допомогою функції WaitForSingleObject здійснюється нескінченне очікування на перехід об’єкта evnt у сигнальний стан.
Якщо у вказану гілку реєстру (Software\IC\RegDemo) внесено зміни, то наш потік отримає відповідний сигнал, і програма видасть відповідне повідомлення (за допомогою функції MessageBox).
Джерела
Дивись також
- Багатонитевість
- Нить
- Синхронізація процесів
- Критична секція
- М'ютекс
- Ф'ютекс, швидкий користувацький м'ютекс