Дополнительные объекты синхронизации
Некоторые объекты Win32 API не предназначены исключительно для целей синхронизации, однако могут использоваться с функциями синхронизации. Такими объектами являются:
Сообщение об изменении папки (change notification)
Windows позволяет организовать слежение за изменениями объектов файловой системы. Для этого служит функция
function FindFirstChangeNotification(
lpPathName: PChar; // Путь к папке, изменения в которой нас
// интересуют
bWatchSubtree: BOOL; // Задает необходимость слежения за
// изменениями во вложенных папках
dwNotifyFilter: DWORD // Фильтр событий
): THandle; stdcall;
Параметр dwNotifyFilter – это битовая маска из одного или нескольких следующих значений:
FILE_NOTIFY_CHANGE_FILE_NAME
Слежение ведется за любым изменением имени файла, в т.ч. созданием и удалением файлов
FILE_NOTIFY_CHANGE_DIR_NAME
Слежение ведется за любым изменением имени папки, в т.ч. созданием и удалением папок
FILE_NOTIFY_CHANGE_ATTRIBUTES
Слежение ведется за любым изменением аттрибутов
FILE_NOTIFY_CHANGE_SIZE
Слежение ведется за изменением размера файлов. Изменение размера происходит при записи в файл. Функция ожидания срабатывает только после успешного сброса дискового кэша
FILE_NOTIFY_CHANGE_LAST_WRITE
Слежение за изменением времени последней записи в файл, т.е., фактически, за любой записью в файл. Функция ожидания срабатывает только после успешного сброса дискового кэша.
FILE_NOTIFY_CHANGE_SECURITY
Слежение за любыми изменениями дескрипторов защиты
Идентификатор, возвращенный этой функцией, может использоваться в любой функции ожидания. Он переходит в сигнальное состояние, когда в папке происходят запрошенные для слежения изменения. Продолжить слежение можно, используя функцию:
function FindNextChangeNotification(
hChangeHandle: THandle
): BOOL; stdcall;
По завершении работы, идентификатор должен быть закрыт при помощи функции:
function FindCloseChangeNotification(
hChangeHandle: THandle
): BOOL; stdcall;
Чтобы не блокировать исполнение основного потока программы функцией ожидания, удобно реализовать ожидание изменений в отдельном потоке. Реализуем поток на базе класса TThread. Для того чтобы можно было прервать исполнение потока методом Terminate необходимо, чтобы функция ожидания, реализованная в методе Execute, также прерывалась при вызове Terminate. Для этого будем использовать вместо WaitForSingleObject функцию WaitForMultipleObjects, и прерывать ожидание по событию (event), устанавливаемому в Terminate.
Code: |
type TCheckFolder = class(TThread) private FOnChange: TNotifyEvent; Handles: array[0..1] of THandle; // Идентификаторы объектов // синхронизации procedure DoOnChange; protected procedure Execute; override; public constructor Create(CreateSuspended: Boolean; PathToMonitor: String; WaitSubTree: Boolean; OnChange: TNotifyEvent; NotifyFilter: DWORD); destructor Destroy; override; procedure Terminate; end;
procedure TCheckFolder.DoOnChange; // Эта процедура вызывается в контексте главного потока приложения // В ней можно использовать вызовы VCL, изменять состояние формы, // например перечитать содержимое TListBox, отображающего файлы begin if Assigned(FOnChange) then FOnChange(Self); end;
procedure TCheckFolder.Terminate; begin inherited; // Вызываем TThread.Terminate, устанавливаем // Terminated = TRUE SetEvent(Handles[1]); // Сигнализируем о необходимости // прервать ожидание end;
constructor TCheckFolder.Create(CreateSuspended: Boolean; PathToMonitor: String; WaitSubTree: Boolean; OnChange: TNotifyEvent; NotifyFilter: DWORD); var BoolForWin95: Integer; begin // Создаем поток остановленным inherited Create(TRUE); // Windows 95 содержит не очень корректную реализацию функции // FindFirstChangeNotification. Для корректной работы, необходимо, // чтобы: // - lpPathName - не содержал завершающего слэша "\" для // некорневого каталога // - bWatchSubtree - TRUE должен передаваться как BOOL(1) if WaitSubTree then BoolForWin95 := 1 else BoolForWin95 := 0; if (Length(PathToMonitor) > 1) and (PathToMonitor[Length(PathToMonitor)] = '\') and (PathToMonitor[Length(PathToMonitor)-1] <> ':') then Delete(PathToMonitor, Length(PathToMonitor), 1); Handles[0] := FindFirstChangeNotification( PChar(PathToMonitor), BOOL(BoolForWin95), NotifyFilter); Handles[1] := CreateEvent(NIL, TRUE, FALSE, NIL); FOnChange := OnChange; // И, при необходимости, запускаем if not CreateSuspended then Resume; end;
destructor TCheckFolder.Destroy; begin FindCloseChangeNotification(Handles[0]); CloseHandle(Handles[1]); inherited; end;
procedure TCheckFolder.Execute; var Reason: Integer; Dummy: Integer; begin repeat // Ожидаем изменения в папке, либо сигнала о завершении // потока Reason := WaitForMultipleObjects(2, @Handles, FALSE, INFINITE); if Reason = WAIT_OBJECT_0 then begin // Изменилась папка, вызываем обработчик в контексте // главного потока приложения Synchronize(DoOnChange); // И продолжаем поиск FindNextChangeNotification(Handles[0]); end; until Terminated; end; |
Поскольку метод TThread.Terminate не виртуальный, этот класс нельзя использовать с переменной типа TThread, т.к. в этом случае будет вызываться Terminate от TThread, который не может прервать ожидания, и поток будет выполняться до изменения в папке, за которой ведется слежение.
Устройство стандартного ввода с консоли (console input)
Идентификатор, стандартного устройства ввода с консоли, полученный при помощи вызова функции GetStdHandle(STD_INPUT_HANDLE), можно использовать в функциях ожидания. Он находится в сигнальном состоянии, если очередь ввода консоли непустая и в несигнальном, если пустая. Это позволяет организовать ожидание ввода символов, либо, при помощи функции WaitForMultipleObjects совместить его с ожиданием каких-то других событий.
Задание (Job)
Job – это новый механизм Windows 2000, позволяющий объединить группу процессов в одно задание и манипулировать ими одновременно. Идентификатор задания находится в сигнальном состоянии, если все процессы, ассоциированные с ним завершились по причине истечения лимита времени на выполнение задания.
Процесс (Process)
Идентификатор процесса, полученный при помощи функции CreateProcess, переходит в сигнальное состояние по завершении процесса. Это позволяет организовать ожидание завершения процесса, например, при запуске из приложения внешней программы.
var
PI: TProcessInformation;
SI: TStartupInfo;
...
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(SI);
Win32Check(CreateProcess(NIL, 'COMMAND.COM', NIL,
NIL, FALSE, 0, NIL, NIL, SI, PI));
// Задерживаем исполнение программы до завершения процесса
WaitForSingleObject(PI.hProcess, INFINITE);
CloseHandle(PI.hProcess);
CloseHandle(PI.hThread);
Следует понимать, что в этом случае вызывающий процесс будет заморожен полностью и не сможет обрабатывать сообщения. Поэтому, если дочерний процесс может выполняться в течение длительного времени, лучше использовать более корректный вариант ожидания, описанный в разделе, посвященном функции MsgWaitForMultipleObjects.
Поток (thread)
Идентификатор потока находится в несигнальном состоянии до тех пор, пока поток выполняется. По его завершении идентификатор переходит в сигнальное состояние. Это позволяет легко узнать, завершился ли поток, либо при помощи функции, ожидающей нескольких объектов, организовать ожидание завершения одного из, либо всех интересующих потоков.
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!