На этом шаге мы рассмотрим синтаксис и использование таких функций.
Иногда требуется задержать выполнение потока до срабатывания одного или всех сразу объектов из группы. Для решения подобной задачи служат функции WaitForMultipleObjects и MsgWaitForMultipleObjects. Рассмотрим их более подробно.
type TWOHandleArray = array[0..MAXIMUM_WAIT_OBJECTS - 1] of THandle; PWOHandleArray = ^TWOHandleArray; function WaitForMultipieObjects( nCount: DWORD; // Количество объектов lpHandles: PWOHandleArray; // Адрес массива объектов bWaitAll: BOOL; // Необходимость ожидания всех // или любого из объектов dwMilliseconds: DWORD // Период ожидания ): DWORD; stdcall;
Эта функция возвращает одно из следующих значений:
Например, в следующем фрагменте кода программа пытается модифицировать два различных ресурса, разделяемых между потоками:
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;
Рассмотрим пример с запуском внешнего приложения. Необходимо, чтобы на время его работы вызывающая программа не реагировала на ввод пользователя, однако ее окно должно продолжать перерисовываться.
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;
Рис.1. Результат работы исходного приложения
Рис.2. Результат работы приложения с заменой исходного текста
Если в потоке, вызывающем функции ожидания явно (с помощью функции CreateWindow) или неявно (используя компонент TForm или технологию DDE или СОМ), создаются окна Windows, поток должен обрабатывать сообщения. Поскольку широковещательные сообщения посылаются всем окнам в операционной системе, поток, не обрабатывающий сообщения, может вызвать взаимную блокировку (операционная система ждет, когда поток обработает сообщение, поток - когда операционная система или другие потоки освободят объект) и привести к зависанию Windows. Если в вашей программе имеются подобные фрагменты, необходимо использовать функцию MsgWaitForMultipleObjects или MsgWaitForMultipleObjectsEx и допускать прерывание ожидания для обработки сообщений. Алгоритм аналогичен вышеприведенному примеру.
На следующем шаге мы рассмотрим прерывание ожидания по запросу .