Объекты синхронизации
Объектами синхронизации называются объекты Windows, идентификаторы которых могут использоваться в функциях синхронизации. Они делятся на две группы – объекты, использующиеся только для синхронизации и объекты, которые используются в других целях, но могут вызывать срабатывание функций ожидания. К первой группе относятся:
Event (событие)
Позволяет известить один или несколько ожидающих потоков о наступлении события. Event бывает
Отключаемый вручную Будучи установленным в сигнальное состояние, остается в нем до тех пор, пока не будет переключен явным вызовом функции ResetEvent
Автоматически отключаемый Автоматически переключается в несигнальное состояние операционной системой, когда один из ожидающих его потоков завершается.
Для создания объекта используется функция:
function CreateEvent(
lpEventAttributes: PSecurityAttributes; // Адрес структуры
// TSecurityAttributes
bManualReset, // Задает, будет Event переключаемым
// вручную (TRUE) или автоматически (FALSE)
bInitialState: BOOL; // Задает начальное состояние. Если TRUE -
// объект в сигнальном состоянии
lpName: PChar // Имя или NIL, если имя не требуется
): THandle; stdcall; // Возвращает идентификатор созданного
// объекта
Структура TSecurityAttributes описана, как:
TSecurityAttributes = record
nLength: DWORD; // Размер структуры, должен
// инициализироваться как
// SizeOf(TSecurityAttributes)
lpSecurityDescriptor: Pointer; // Адрес дескриптора защиты. В
// Windows 95 и 98 игнорируется
// Обычно можно указывать NIL
bInheritHandle: BOOL; // Задает, могут ли дочерние
// процессы наследовать объект
end;
Если не требуется задание особых прав доступа под Windows NT или возможности наследования объекта дочерними процессами, в качестве параметра lpEventAttributes можно передавать NIL. В этом случае объект не может наследоваться дочерними процессами и ему задается дескриптор защиты «по умолчанию».
Параметр lpName позволяет разделять объекты между процессами. Если lpName совпадает с именем уже существующего объекта типа Event, созданного текущим или любым другим процессом, функция не создает нового объекта, а возвращает идентификатор уже существующего. При этом игнорируются параметры bManualReset, bInitialState и lpSecurityDescriptor. Проверить, был объект создан, или используется уже существующий можно следующим образом:
hEvent := CreateEvent(NIL, TRUE, FALSE, 'EventName');
if hEvent = 0 then
RaiseLastWin32Error;
if GetLastError = ERROR_ALREADY_EXISTS then begin
// Используем ранее созданный объект
end;
Если объект используется для синхронизации внутри одного процесса, его можно объявить как глобальную переменную и создавать без имени.
Имя объекта не должно совпадать с именем любого из существующих объектов типов Semaphore, Mutex, Job, Waitable Timer или FileMapping. В случае совпадения имен, функция возвращает ошибку.
Если известно, что Event уже создан, для получения доступа к нему можно вместо CreateEvent воспользоваться функцией:
function OpenEvent(
dwDesiredAccess: DWORD; // Задает права доступа к объекту
bInheritHandle: BOOL; // Задает, может ли объект наследоваться
// дочерними процессами
lpName: PChar // Имя объекта
): THandle; stdcall;
Функция возвращает идентификатор объекта, либо 0, в случае ошибки. Параметр dwDesiredAccess может принимать одно из следующих значений:
EVENT_ALL_ACCESS Приложение получает полный доступ к объекту
EVENT_MODIFY_STATE Приложение может изменять состояние объекта функциями SetEvent и ResetEvent
SYNCHRONIZE Только для Windows NT – приложение может использовать объект только в функциях ожидания
После получения идентификатора можно приступать к его использованию. Для этого имеются следующие функции:
function SetEvent(hEvent: THandle): BOOL; stdcall;
Устанавливает объект в сигнальное состояние
function ResetEvent(hEvent: THandle): BOOL; stdcall;
Сбрасывает объект, устанавливая его в несигнальное состояние
function PulseEvent(hEvent: THandle): BOOL; stdcall
Устанавливает объект в сигнальное состояние, дает отработать всем функциям ожидания, ожидающим этот объект, а затем снова сбрасывает его.
В WinAPI события используются, для выполнения операций асинхронного ввода-вывода. Следующий пример показывает, как приложение инициирует запись одновременно в два файла, а затем ожидает завершения записи перед продолжением работы. Такой подход может обеспечить более высокую производительность при высокой интенсивности ввода-вывода, чем последовательная запись.
Code: |
var Events: array[0..1] of THandle; // Массив объектов синхронизации Overlapped: array[0..1] of TOverlapped;
...
// Создаем объекты синхронизации Events[0] := CreateEvent(NIL, TRUE, FALSE, NIL); Events[1] := CreateEvent(NIL, TRUE, FALSE, NIL);
// Инициализируем структуры TOverlapped FillChar(Overlapped, SizeOf(Overlapped), 0); Overlapped[0].hEvent := Events[0]; Overlapped[1].hEvent := Events[1];
// Начинаем асинхронную запись в файлы WriteFile(hFirstFile, FirstBuffer, SizeOf(FirstBuffer), FirstFileWritten, @Overlapped[0]); WriteFile(hSecondFile, SecondBuffer, SizeOf(SecondBuffer), SecondFileWritten, @Overlapped[1]);
// Ожидаем завершения записи в оба файла WaitForMultipleObjects(2, @Events, TRUE, INFINITE);
// Уничтожаем объекты синхронизации CloseHandle(Events[0]); CloseHandle(Events[1]); |
По завершении работы с объектом, он должен быть уничтожен функцией CloseHandle.
Delphi предоставляет класс TEvent, инкапсулирующий функциональность объекта Event. Класс расположен в модуле SyncObjs.pas и объявлен следующим образом:
Code: |
type TWaitResult = (wrSignaled, wrTimeout, wrAbandoned, wrError);
TEvent = class(THandleObject) public constructor Create(EventAttributes: PSecurityAttributes; ManualReset, InitialState: Boolean; const Name: string); function WaitFor(Timeout: DWORD): TWaitResult; procedure SetEvent; procedure ResetEvent; end; |
Назначение методов очевидно из их названий. Использование этого класса позволяет не вдаваться в тонкости реализации вызываемых функций Windows API. Для простейших случаев объявлен еще один класс с упрощенным конструктором.
Code: |
type TSimpleEvent = class(TEvent) public constructor Create; end;
…
constructor TSimpleEvent.Create; begin FHandle := CreateEvent(nil, True, False, nil); end; |
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!