Содержание материала

 

Дополнительные объекты синхронизации

Некоторые объекты 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)

Идентификатор потока находится в несигнальном состоянии до тех пор, пока поток выполняется. По его завершении идентификатор переходит в сигнальное состояние. Это позволяет легко узнать, завершился ли поток, либо при помощи функции, ожидающей нескольких объектов, организовать ожидание завершения одного из, либо всех интересующих потоков.

Добавить комментарий

Не использовать не нормативную лексику.

Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.

ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!


Защитный код
Обновить