Нитка (інформатика)

Матеріал з Вікіпедії — вільної енциклопедії.
(Перенаправлено з Нить)
Перейти до: навігація, пошук
Процес з двома нитями виконання

Нить (англ. thread) або повніше нить виконання (англ. thread of execution), часто застосовується назва потік (програмний потік) та англіцизм тред — в інформатиці так називається спосіб програми розщепити себе на дві чи більше одночасні (чи псевдо-одночасні) задачі. Реалізація нитей та процесів відрізняються в різних операційних системах, але загалом нить міститься всередині процесу і різні ниті одного процесу спільно розподіляють деякі ресурси, в той час як різні процеси ресурси не розподіляють.

В системах з одним процесором багатонитевість реалізується загалом поділом часу виконання («кванти часу»), дуже подібно до паралельного виконання багатьох задач: процесор послідовно переключається між різними нитями. Це переключення контексту відбувається настільки швидко, що у кінцевого користувача створюється ілюзія одночасного виконання. На багатопроцесорних чи на багатоядерних системах робота нитей здійснюється справді одночасно, бо різні ниті і процеси виконуються буквально одночасно різними процесорами або ядрами процесора. Особливості квантування в опраційній системі Windows розглянуто у статті Квант (Windows).

Багато сучасних операційних систем прямо підтримують квантування часу і багатопроцесорну роботу нитей через планувальник процесів. Ядро операційної системи дозволяє програмісту маніпулювати нитями через інтерфейс системних викликів. Деякі реалізації викликають ниті ядра, оскільки легковагові процеси (англ. lightweight process, LWP) є спеціальним типом нитей ядра, що розподіляють деякі стани і інформацію.

Поза тим, програма може емулювати роботу нитей, використовуючи таймер, сигнали або інші методи, щоб перервати власне виконання і послідовно виконувати різні задачі власним квантуванням часу. Такий спосіб іноді зветься нитями користувацького простору (англ. user-space threads) або волокнами.

Зауваження щодо термінології[ред.ред. код]

"Англо-український тлумачний словник з обчислювальної техніки, інтернету і програмування" (Київ, видавництво "СофтПрес", 2005), виданий за підтримки компанії Microsoft, в першу чергу подає переклад оригінального терміну thread українською мовою як потік (ст. 494).

Порівняння нитей з процесами[ред.ред. код]

Ниті відрізняються від традиційних процесів багатозадачних операційних систем, в тому що процеси:

Ниті всередині процесу, з іншої сторони, розподіляють інформацію про стан процесу, і прямо доступаються до спільної пам'яті та інших ресурсів. Переключення контексту між нитями процеса на загал швидше, ніж переключення контексту між процесами. Описуючи ситуацію такі системи, як Windows NT та OS/2, кажуть, що мають «дешеві» ниті та «дорогі» процеси; в інших операційних системах ситуація не дуже відмінна.

Ниті вимагають більшої кваліфікації програмістів[ред.ред. код]

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

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

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

Традиційно базове комп'ютерне апаратне забезпечення не надавало багато підтримку багатонитевості, оскільки переключення між нитями загалом є вже скорішою процедурою порівняно з переключенням контексту процесів. Процесори вбудованих систем, що мають більші вимоги до поведінки в реальному часі, можуть мати апаратну підтримку багатонитевості зменшенням часу перемикання, можливо розміщуючи виділений блок регістрів для кожної ниті, замість того, щоб зберігати/читати загальні регістри. На кінці 1990-х стала відома ідея виконання інструкцій з різних нитей одночасно, це зветься одночасною багатонитевостю. Ця можливість була введена в процесорі Intel Pentium 4 під ім'ям Hyper-threading.

Процеси, ниті і волокна[ред.ред. код]

Процес є «найважчим» об'єктом для планувальника ядра операційної системи. Власні ресурси процесу розміщені операційною системою. Ресурси включають пам'ять, відкриті файли та пристрої, сокети, та вікна. Процеси не розділяють з кимось адресний простір чи залучені файли, за винятком явних методів, таких як успадкування файлів чи сегментів спільної пам'яті, або робота з файлами в режимі спільного доступу. Типово, процеси запобіжно багатозадачні. Але Windows 3.1 і старі версії Mac OS використовували кооперативну багатозадачність.

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

В деяких ситуаціях є різниця між «нитями ядра» та «нитями користувача» — перші управляються і плануються ядром, другі управляються і плануються в просторі користувача. В цій статті термін «нить» використовується для позначення поняття «нить ядра», а поняття «волокно» (англ. fiber) означає нить користувача. Волокна мають кооперативне планування: працююче волокно має явно зупинитися і передати управління іншому волокну. Волокно може бути запущено будь-якою ниттю в одному процесі.

Проблеми нитей та волокон[ред.ред. код]

Паралелізм та структури даних[ред.ред. код]

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

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

Введення-виведення і планування[ред.ред. код]

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

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

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

Win32 забезпечує програмний інтерфейс волокон[1]. SunOS 4.x реалізує волокна під назвою «легковагові процеси» (англ. light-weight processes, LWP), відомі також як «зелені ниті». NetBSD 2.x+, і DragonFly BSD також реалізають LWP як ниті (модель 1:1). SunOS від версій 5.2 до 5.8, NetBSD версій від 2 до 4 реалізує дворівневу модель, дозволяючи одному чи більше користувацьких волокон на кожну нить ядра (модель M:N). SunOS 5.9 і пізніші, а також NetBSD 5 позбавлені підтримки волокон, повернулися до моделі 1:1.[2]

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

Моделі[ред.ред. код]

1:1[ред.ред. код]

Користувацькі ниті моделі 1:1 відповідають одна до одного планувальному об'єктові ядра. Це найпростіша можлива реалізація нитей. В Linux цей підхід реалізує звична бібліотека C (NPTL).

N:M[ред.ред. код]

N:M розкладає N нитей застосунку на M об'єктів ядра, або «віртуальних процесорів». Така реалізація є компромісом між реалізацією нитей рівня ядра («1:1») і рівнем користувача («N:1»). Загалом, нитева система «N:M» складніша в реалізації і за ядерну, і за користувацьку системи, бо потрібні зміни і в ядрі і з боку користувача. В реалізації m×n, бібліотека нитей відповідальна за планування нитей користувача на можливі об'єкти планування; це робить переключення контексту нитей дуже швидким, бо не потрібні системні виклики. Проте, це збільшує складність і ймовірність інверсії пріоритетів, а також вимагає екстенсивної (і дорогої) координації між планувальниками ядра та користувача.

N:1[ред.ред. код]

Модель N:1 означає відобразити всі ниті застосунку на єдиний планувальний об'єкт ядра; оскільки ядро нічого не знає про користувацькі ниті, фактично це є реалізація волокон. При такому підході переключення контексту може бути зроблене дуже швидко і, на додаток, воно може бути реалізоване на простому ядрі без підтримки нитей. Один з найбільших недоліків, це те, що неможливо отримати користь від апаратного пришвидшення на багатоядерних процесорах або багато-процесорних комп'ютерах: у цій моделі неможливо запустити більше однієї ниті одночасно.

Реалізації[ред.ред. код]

Існує багато різних (і несумісних) реалізацій нитей, включаючи реалізації на рівні ядра і на рівні користувача.

Звісно, волокна можуть бути реалізовані поза підтримкою з боку операційної системи, хоча деякі операційні системи або бібліотеки забезпечують їхню явну підтримку. Наприклад, Microsoft Windows (Windows NT 3.51 SP3 і пізніші) підтримують програмний інтерфейс волокон для застосунків, тож за бажання, можна спробувати підвищити продуктивність програми самому, замість покладатися на планувальник ядра (який може бути не налаштований для застосунку). Прикладом може бути планувальник користувацького режиму в Microsoft SQL Server 2000, який працює в просторі волокон.

Приклади реалізацій рівня ядра[ред.ред. код]

Приклади реалізації рівня користувача[ред.ред. код]

Приклади гібридних реалізацій[ред.ред. код]

  • Scheduler activations використовується бібліотекою рідних-POSIX нитей NetBSD (модель N:M на противагу моделі ядра 1:1 та моделі користувацького простору)
  • Marcel з проекту PM2.

Реалізація в Windows[ред.ред. код]

Докладніше: Ниті у Windows

Visual C/C++ реалізує інтерфейс нитей двома способами

  • через функція CreateThread і споріднені (варіант: AfxBeginThread і споріднені)
  • через функції _beginthread, _beginthreadex і споріднені

Перша реалізація є дефектною і призводить до витоку пам'яті при використанні в обробниках нитей старих функцій стандартної бібліотеки.[3]

Існують реалізації стандарту POSIX pthread для Windows[4] як надбудова над інтерфейсом _beginthread.

Виноски[ред.ред. код]

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

Посилання[ред.ред. код]