Планування потоків

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Планування потоків (ниток) — це системний алгоритм керування послідовністю виконання потоків виконання та розподілу процесорного часу між ними.

Витісняюче планування[ред. | ред. код]

У Windows реалізовано підсистему витісняючого планування ниток на основі рівнів пріоритету, у якій завжди виконується готова до виконання нитка з найбільшим пріоритетом[1]. Але вибір нитки для виконання може бути обмежений набором процесорів, на яких вона може виконуватися. Це явище називають прив'язкою до процесорів (processor affinity). За замовчуванням нить виконується на будь-якому доступному процесорі, але можна змінити прив'язку до процесорів через функції планування.

Обрана для виконання нитка працює протягом певного періоду, який називають квантом. Квант визначає, скільки часу буде виконуватися нитка, поки не настане черга іншої нитки з тим же пріоритетом (або вищим). Тривалість квантів залежить від трьох факторів: конфігураційних параметрів системи (довгі або короткі кванти), статусу процесу (активний або фоновий) і використання об'єкта "завдання" для зміни тривалості квантів. Однак нитка може не повністю використати свій квант. Оскільки у Windows реалізовано витісняючий планувальник, то як тільки інша нитка з вищим пріоритетом готова до виконання, поточна нитка витісняється, навіть якщо її квант ще не минув. Тому нитка може бути обрана для виконання і тут же витіснена, не скориставшись своїм квантом. Код Windows, відповідальний за планування, реалізований у ядрі. Оскільки цей код розосереджений по ядру, єдиного модуля або процедури з назвою "планувальник" немає. Сукупність процедур, що виконують ці обов'язки, називають диспетчером ядра (kernel's dispatcher).

Сценарії планування ниток у Windows[ред. | ред. код]

Самостійне перемикання[ред. | ред. код]

Самостійне перемикання ниток

Нитка може самостійно звільнити процесор, перейшовши в стан очікування на якому-небудь об'єкті (наприклад, події, м'ютексі, семафорі, процесі, потоці, віконному повідомленні тощо) шляхом виклику однієї із функцій очікування (наприклад, WaitForSingieObject або WaitForMultipleObjects).

При цьому нитка входить у стан очікування, а Windows вибирає нову нитку для виконання[2]. Активна нитка (верхній кружок на рисунку) самостійно звільняє процесор, у результаті чого до процесора під'єднується інша нитка із черги (відзначена зірочкою у стовпчику Running). При цьому пріоритет нитки, що звільняє процесор, не знижується — він просто переводиться в чергу очікування обраних ним об'єктів. Коли нитка входить у стан очікування, залишкова доля кванта не втрачається. Після успішного завершення очікування квант нитки зменшується на одну одиницю, що еквівалентно третині інтервалу таймера (виняток становлять нитки із пріоритетом від 14 і вище, у яких після очікування квант скидається).

Витіснення[ред. | ред. код]

Планування ниток з витісненням

У цьому сценарії нитка з нижчим пріоритетом витісняється готовою до виконання ниткою з вищим пріоритетом[2]. Така ситуація може бути наслідком двох обставин:

  • завершилося очікування нитки з вищим пріоритетом (тобто відбулася подія, якої вона чекала);
  • пріоритет нитки збільшився або зменшився.

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

Нитки користувацького режиму можуть витісняти нитки режиму ядра. Тобто режим виконання нитки значення не має — визначальним фактором є її пріоритет.

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

Завершення кванта[ред. | ред. код]

Планування ниток у момент завершення кванта поточної нитки

Коли нитка витратить свій квант процесорного часу, Windows повинна вирішити, чи знижувати її пріоритет і під’єднати до процесора іншу нитку[2]. Знизивши пріоритет нитки, Windows шукає доречну для виконання нитку (такою є будь-яка нитка із черги готових, пріоритет якої вищий нового пріоритету поточної нитки). Якщо Windows не змінює пріоритет нитки і в черзі готових ниток є інші з тим же пріоритетом, вона вибирає із черги наступну нитку з таким же пріоритетом, а нитку, що виконувалася до цього, переміщає у хвіст черги (задаючи їй нову величину кванта й переводячи її зі стану Running у стан Ready). Якщо жодна нитка з тим же пріоритетом не готова до виконання, активній нитці виділяється ще один квант процесорного часу.

Завершення нитки[ред. | ред. код]

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

Перемикання контексту[ред. | ред. код]

Контекст нитки й процедура його перемикання залежать від архітектури процесора. У типовому випадку перемикання контексту вимагає збереження й відновлення наступних даних:

  • вказівника команд;
  • вказівників на стек ядра і стек користувача;
  • вказівника на адресний простір, у якому виконується нитка (каталог таблиць сторінок пам’яті процесу).

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

Динамічне підвищення пріоритету[ред. | ред. код]

Windows може динамічно підвищувати значення поточного пріоритету ниті в одному з таких випадків[2]:

  • після завершення операцій вводу-виводу;
  • після закінчення очікування на події або семафорі виконавчої системи;
  • після закінчення операції очікування нитями активного процесу;
  • при пробудженні GUI-нитей через операції з вікнами;
  • якщо нить, готова до виконання, затримується через нестачу процесорного часу.

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

Windows ніколи не збільшує пріоритет нитей у діапазоні реального часу (16-31). Тому планування таких нитей стосовно інших завжди передбачуване.

Джерела[ред. | ред. код]

  1. Руссинович М. Внутреннее устройство Microsoft Windows : Windows Server 2003, Windows XP и Windows 2000. Мастер-класс / М.Руссинович, Д.Соломон ; пер. с англ. – 4-е изд. – М : Издательско-торговый дом "Русская редакция" ; СПб : Питер, 2005.
  2. а б в г Коноваленко І.В., Федорів П.С. Системне програмування у Windows з прикладами на Delphi, Т:ТНТУ.- 2012.

Див. також[ред. | ред. код]