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

Функции, ожидающие нескольких объектов

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

 

type

TWOHandleArray = array[0..MAXIMUM_WAIT_OBJECTS - 1] of THandle;

PWOHandleArray = ^TWOHandleArray;

 

function WaitForMultipleObjects(

nCount: DWORD;              // Задает количество объектов

lpHandles: PWOHandleArray;  // Адрес массива объектов

bWaitAll: BOOL;             // Задает, требуется ожидание всех

                             // объектов или любого

dwMilliseconds: DWORD       // Период ожидания

): DWORD; stdcall;

 

Функция возвращает одно из следующих значений:

Число в диапазоне от WAIT_OBJECT_0 до WAIT_OBJECT_0 + nCount 1        Если bWaitAll равно TRUE, то это число означает, что все объекты перешли в сигнальное состояние. Если FALSE то, вычтя из возвращенного значения WAIT_OBJECT_0, мы получим индекс объекта в массиве lpHandles.        

Число в диапазоне от WAIT_ABANDONED_0 до WAIT_ABANDONED_0 + nCount 1        Если bWaitAll равно TRUE это означает, что все перешли в сигнальное состояние, но хотя бы один из владевших ими потоков завершился, не сделав объект сигнальным. Если FALSE то, вычтя из возвращенного значения WAIT_ABANDONED_0,  мы получим индекс объекта в массиве lpHandles, поток, владевший которым, завершился, не сделав его сигнальным.        

WAIT_TIMEOUT        Истек период ожидания        

WAIT_FAILED        Произошла ошибка        

Например, в следующем фрагменте кода программа пытается модифицировать два различных ресурса, разделяемых между потоками.

Code:

var

Handles: array[0..1] of THandle;

Reason: DWORD;

RestIndex: Integer;

 

...

 

Handles[0] := OpenMutex(SYNCHRONIZE, FALSE, 'FirstResource');

Handles[1] := OpenMutex(SYNCHRONIZE, FALSE, 'SecondResource');

// Ждем первого из объектов

Reason := WaitForMultipleObjects(2, @Handles, FALSE, INFINITE);

case Reason of

WAIT_FAILED: RaiseLastWin32Error;

WAIT_OBJECT_0, WAIT_ABANDONED_0:

   begin

     ModifyFirstResource;

     RestIndex := 1;

   end;

WAIT_OBJECT_0 + 1, WAIT_ABANDONED_0 + 1:

   begin

     ModifySecondResource;

     RestIndex := 0;

   end;

// WAIT_TIMEOUT возникнуть не может

end;

// Теперь ожидаем освобождения следующего объекта

if WailForSingleObject(Handles[RestIndex],

    INFINITE) = WAIT_FAILED then

      RaiseLastWin32Error;

// Дождались, модифицируем оставшийся ресурс.

if RestIndex = 0 then

ModifyFirstResource

else

ModifySecondResource;

 

 

Описанную выше технику можно применять, если Вы точно знаете, что задержка ожидания объекта окажется небольшой. В противном случае Ваша программа окажется "замороженной" и не сможет даже перерисовать своё окно. Если период задержки может оказаться значительным, то необходимо дать программе возможность реагировать на сообщения Windows. Выходом может служить использование функций с ограниченным периодом ожидания (и повторный вызов, в случае возврата WAIT_TIMEOUT), либо использование функции:

 

function MsgWaitForMultipleObjects(

nCount: DWORD;     // количество объектов синхронизации

var pHandles;      // адрес массива объектов

fWaitAll: BOOL;    // Задает, требуется ожидание всех

                    // объектов или любого

dwMilliseconds,    // Период ожидания

dwWakeMask: DWORD  // Тип события, прерывающего ожидание

): DWORD; stdcall;

 

Главное отличие этой функции от предыдущей параметр dwWakeMask, который является комбинацией битовых флагов QS_XXX и задает типы сообщений, которые прерывают ожидание функции, независимо от состояния ожидаемых объектов. Например, маска QS_KEY позволяет прервать ожидание при появлении в очереди сообщений WM_KEYUP, WM_KEYDOWN, WM_SYSKEYUP или WM_SYSKEYDOWN, а маска QS_PAINT - сообщения WM_PAINT. Полный список значений, допустимых для dwWakeMask имеется в документации по Windows SDK. При появлении в очереди потока, вызвавшего функцию, сообщений, соответствующих заданной маске функция возвращает значение WAIT_OBJECT_0 + nCount. Получив это значение, Ваша программа может обработать его и снова вызвать функцию ожидания. Рассмотрим пример с запуском внешнего приложения. Необходимо, чтобы на время его работы вызывающая программа не реагировала на ввод пользователя, однако её окно должно продолжать перерисовываться.

Code:

procedure TForm1.Button1Click(Sender: TObject);

var

PI: TProcessInformation;

SI: TStartupInfo;

Reason: DWORD;

Msg: TMsg;

begin

// Инициализируем структуру 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);

// и посмотреть, как будет реагировать программа на

// перемещение других окон над её окном

//**************************************************

repeat

   // Ожидаем завершения дочернего процесса или сообщения

   // перерисовки WM_PAINT

   Reason := MsgWaitForMultipleObjects(1, PI.hProcess, FALSE,

     INFINITE, QS_PAINT);

   if Reason = WAIT_OBJECT_0 + 1 then begin

     // В очереди сообщений появился WM_PAINT – Windows

     // требует обновить окно программы.

     // Удаляем сообщение из очереди

     PeekMessage(Msg, 0, WM_PAINT, WM_PAINT, PM_REMOVE);

     // И перерисовываем наше окно

     Update;

   end;

   // Повторяем цикл, пока не завершится дочерний процесс

until Reason = WAIT_OBJECT_0;

// Удаляем из очереди накопившиеся там сообщения

while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do;

CloseHandle(PI.hProcess);

CloseHandle(PI.hThread)

end;

 

 

Если в потоке, вызывающем функции ожидания явно (функцией CreateWindow) или неявно (используя TForm, DDE, COM) создаются окна Windows поток должен обрабатывать сообщения. Поскольку широковещательные сообщения посылаются всем окнам в системе поток, не обрабатывающий сообщения может вызвать взаимоблокировку, (система ждет, когда поток обработает сообщение, поток когда система или другие потоки освободят объект) и привести к зависанию Windows. Если в Вашей программе имеются подобные фрагменты необходимо использовать MsgWaitForMultipleObjects или MsgWaitForMultipleObjectsEx и позволять прервать ожидание для обработки сообщений. Алгоритм аналогичен вышеприведенному примеру.

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

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

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

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


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