Функции, ожидающие нескольких объектов
Иногда требуется задержать выполнение потока до срабатывания одного или всех сразу из группы объектов. Для решения подобной задачи служат следующие функции:
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 и позволять прервать ожидание для обработки сообщений. Алгоритм аналогичен вышеприведенному примеру.
Просьба писать ваши замечания, наблюдения и все остальное,
что поможет улучшить предоставляемую информацию на этом сайте.
ВСЕ КОММЕНТАРИИ МОДЕРИРУЮТСЯ ВРУЧНУЮ, ТАК ЧТО СПАМИТЬ БЕСПОЛЕЗНО!