Програмування для windows nt

Почнемо з простого: розповімо про те, як завдання може дочекатися завершення іншого завдання або завершення процесу.

Якщо вам потрібно зробити так, щоб одна задача чекала завершення іншого завдання, що працює в рамках того ж процесу, ви можете впасти в спокусу виконати таке очікування за допомогою глобальної змінної, вміст якої змінюється при завершенні роботи другого завдання. Перше завдання могла б при цьому опитувати вміст цієї глобальної змінної в циклі, чекаючи моменту, коли воно зміниться.

Очевидний недолік такого підходу полягає в тому, що очікує завдання отримує кванти процесорного часу. При цьому знижується загальна продуктивність системи, тому необхідно знайти інший спосіб виконання очікування. Було б добре організувати очікування таким чином, щоб планувальник завдань не виділяв процесорний час тим завданням, які чогось чекають.

Очікування завершення завдання або процесу

Якщо потрібно дочекатися завершення одного завдання або одного процесу, найкраще скористатися для цього функцією WaitForSingleObject XE "WaitForSingleObject". з якої ви вже знайомі з попередніх програм.

Прототип функції WaitForSingleObject представлений нижче:

Як параметр hObject цієї функції потрібно передати ідентифікатор об'єкта, для якого виконується очікування, а в якості параметра dwTimeout - час очікування в мілісекундах (очікування може бути і нескінченним, якщо для часу вказати значення INFINITE).

Багато об'єктів операційної системи Microsoft Windows NT, такі, наприклад, як ідентифікатори завдань, процесів, файлів, можуть перебувати в двох станах - зазначеному (signaled) і невідзначеними (nonsignaled). Зокрема, якщо завдання або процес знаходяться в стані виконання (тобто працюють), відповідні ідентифікатори знаходяться в невідзначеними стані. Коли ж завдання або процес завершують свою роботу, їх ідентифікатори відзначаються (тобто переходять в зазначене стан).

Якщо завдання створює іншу задачу або процес, і потім викликає функцію WaitForSingleObject XE "WaitForSingleObject". вказавши їй в якості першого параметра ідентифікатор створеної завдання, а в якості другого - значення INFINITE XE "INFINITE". батьківська задача переходить в стан очікування. Вона буде знаходитися в стані очікування до тих пір, поки дочірня завдання або процес не завершить свою роботу.

Зауважимо, що функція WaitForSingleObject не перевіряє стан ідентифікатора дочірньої завдання або процесу в циклі, чекаючи її (або його) завершення. Така дія призвело б до того, що батьківської завданню виділялися б кванти процесорного часу, а це якраз те, чого ми хотіли б уникнути. Замість цього функція WaitForSingleObject повідомляє планувальником завдань, що виконання батьківської завдання, що викликала цю функцію, необхідно призупинити до тих пір, поки дочірня завдання або процес не завершить свою роботу.

Приймаючи рішення про виділення батьківської завданню кванта процесорного часу, планувальник перевіряє стан дочірньої завдання. Квант часу виділяється планувальником батьківської завданню тільки в тому випадку, якщо дочірня завдання завершила свою роботу і її ідентифікатор знаходиться в зазначеному стані. Така перевірка не забирає багато часу і, отже, не призводить до помітного зниження продуктивності системи.

На рис. 4.1 показано, як завдання з номером 1 очікує завершення завдання з номером 2, яка має код hThread2.

Пунктирною стрілкою тут показано подія, яка веде до завершення очікування. Цією подією, очевидно, є завершення роботи завдання hThread2.

Як приклад використання функції WaitForSingleObject XE "WaitForSingleObject" для очікування завершення дочірнього процесу, розглянемо трохи змінений фрагмент вихідного тексту програми PSTART, описаного в попередньому розділі:

Тут головне завдання за допомогою функції CreateProcess запускає процес. Ідентифікатор цього процесу зберігається в поле hProcess структури pi. Якщо запуск процесу стався успішно, головне завдання програми призупиняє свою роботу до тих пір, поки запущений процес не завершить свою роботу. Для цього вона викликає функцію WaitForSingleObject XE "WaitForSingleObject". передаючи їй ідентифікатор запущеного процесу.

Якщо батьківський процес не цікавиться долею свого дочірнього процесу, функція WaitForSingleObject не потрібна. У цьому випадку головне завдання може відразу закрити ідентифікатори дочірнього процесу і головного завдання дочірнього процесу, обірвавши "батьківські узи":

Запущений таким чином процес називається від'єднання (detached). Він буде жити своїм життям незалежно від стану запустив його процесу.

Тепер поговоримо про код завершення функції WaitForSingleObject.

У разі помилки функція повертає значення WAIT_FAILED. При цьому код помилки можна отримати за допомогою функції GetLastError.

Якщо ж функція завершилася успішно, вона може повернути одну з наступних трьох значень: WAIT_OBJECT_0, WAIT_TIMEOUT або WAIT_ABANDONED.

Якщо стан ідентифікатора об'єкта, для якого виконувалося очікування, стало зазначеним, функція фозвращает значення WAIT_OBJECT_0. Таким чином, коли ми очікуємо завершення завдання і завдання завершилася "природним чином", функція WaitForSingleObject XE "WaitForSingleObject" поверне саме це значення.

Якщо час очікування, заданий в другому параметрі функції WaitForSingleObject минув, але об'єкт так і не перейшов в зазначене стан, повертається значення WAIT_TIMEOUT. Очевидно, при нескінченному очікуванні ви ніколи не отримаєте цей код завершення.

Код завершення WAIT_ABANDONED повертається для об'єкта синхронізації типу Mutex (його ми розглянемо пізніше), який не був звільнений завданням, що завершила свою роботу. Таким чином, в цьому випадку очікування було скасовано і відповідний об'єкт (Mutex) не перейшов в зазначене стан.

Очікування завершення декількох завдань або процесів

Часто одна задача повинна чекати завершення усіх завдань або процесів, або однієї з кількох завдань або процесів. Таке очікування неважко виконати за допомогою функції WaitForMultipleObjects, прототип якої наведено нижче:

Якщо вміст параметра fWaitAll одно TRUE, завдання переводиться в стан очікування до тих пір, поки всі завдання або процеси, ідентифікатори яких зберігаються в масиві lphObjects, не завершать свою роботу. У тому випадку, коли значення параметра fWaitAll одно FALSE, очікування припиняється, коли одна із зазначених завдань або процесів завершить свою роботу. Для виконання нескінченного очікування, як і в випадку функції WaitForSingleObject XE "WaitForSingleObject". через параметр dwTimeout слід передати значення INFINITE.

Як користуватися цією функцією?

Приклад ви можете знайти в початкових текстах пріложеенія MultiSDI, описаного раніше.

Перш за все вам необхідно підготувати масив для зберігання ідентифікаторів завдань або процесів, завершення яких потрібно дочекатися:

Після цього в масив слід записати ідентифікатори запущених завдань або процесів:

В даному випадку завдання, яка викликала функцію WaitForMultipleObjects, перейде в стан очікування до тих пір, поки всі три завдання не завершать свою роботу.

Функція WaitForMultipleObjects може повернути одне з наступних значень:

· WAIT_FAILED (при помилці);

· WAIT_TIMEOUT (якщо час очікування минув);

· Значення в діапазоні від WAIT_OBJECT_0 до (WAIT_OBJECT_0 + cObjects - 1), яке, в залежності від вмісту параметра fWaitAll, або означає що всі очікувані ідентифікатори перейшли в зазначене стан (якщо fWaitAll дорівнював TRUE), або це значення, якщо з нього відняти константу WAIT_OBJECT_0, так само індексу ідентифікатора зазначеного об'єкта в масиві ідентфікаторов lphObjects.

· Значення в діапазоні від WAIT_ABANDONED_0 до (WAIT_ABANDONED_0 + cObjects - 1), якщо очікування для об'єктів Mutex було скасовано. Індекс відповідного об'єкта в масиві lphObjects можна визначити, якщо відняти від коду повернення значення WAIT_ABANDONED_0.

Схожі статті