Багатопоточність

Не слід плутати з Потік даних.

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

Такі потоки називають також потоками виконання (від англ. thread of execution ); Іноді називають "нитками" (буквальний переклад англ. thread ) Або неформально "тред".

Суттю багатопоточності є квазімногозадачность на рівні одного виконуваного процесу, тобто всі потоки виконуються в адресному просторі процесу. Крім цього, всі потоки процесу мають не тільки загальний адресний простір, але і загальні дескриптори файлів. Виконується процес має як мінімум один (головний) потік.

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

До достоїнств багатопоточності в програмуванні можна віднести наступне:

  • Спрощення програми в деяких випадках за рахунок використання загального адресного простору.
  • Менші щодо процесу тимчасові витрати на створення потоку.
  • Підвищення продуктивності процесу за рахунок розпаралелювання процесорних обчислень і операцій введення / виводу.

1. Типи реалізації потоків

  • Потік в просторі користувача. Кожен процес має таблицю потоків, аналогічну таблиці процесів ядра.

Переваги і недоліки цього типу наступні: Недоліки

  1. Відсутність переривання по таймеру усередині одного процесу
  2. При використанні блокуючого системного запиту для процесу всі його потоки блокуються.
  3. Складність реалізації
  • Потік в просторі ядра. Поряд з таблицею процесів в просторі ядра є таблиця потоків.
  • "Волокна" ( англ. fibers ). Кілька потоків режиму користувача, що виконуються в одному потоці режиму ядра. Потік простору ядра споживає помітні ресурси, в першу чергу фізичну пам'ять і діапазон адрес режиму ядра для стека режиму ядра. Тому було введено поняття "волокна" - полегшеного потоку, виконуваного виключно в режимі користувача. У кожного потоку може бути кілька "волокон".

2. Взаємодія потоків

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

  • Взаємовиключення (mutex, мьютекс) - це об'єкт синхронізації, який встановлюється в особливу сигнальний стан, коли не зайнятий яким-небудь потоком. Тільки один потік володіє цим об'єктом у будь-який момент часу, звідси й назва таких об'єктів (від англійського mut ually ex clusive access - взаємно виключає доступ) - одночасний доступ до загального ресурсу виключається. Після всіх необхідних дій мьютекс звільняється, надаючи іншим потокам доступ до загального ресурсу. Об'єкт може підтримувати рекурсивний захоплення другий раз тим же потоком, збільшуючи лічильник, не блокуючи потік, і вимагаючи потім багаторазового звільнення. Такий, наприклад, mutex в Win32 і KMUTEX в ядрі Windows. Проте є і такі реалізації, які не підтримують таке і призводять до взаємної блокуванні потоку при спробі рекурсивного захоплення. Це FAST_MUTEX в ядрі Windows і критична секція в Win32.
  • Семафори являють собою доступні ресурси, які можуть бути придбані декількома потоками в один і той же час, поки пул ресурсів не спорожніє. Тоді додаткові потоки повинні чекати, поки необхідну кількість ресурсів не буде знову доступно. Семафори дуже ефективні, оскільки вони дозволяють одночасний доступ до ресурсів. Семафор є логічне розширення мьютекса - семафор з лічильником 1 еквівалентний мьютекс, але лічильник може бути і більше 1.
  • Події. Об'єкт, що зберігає в собі 1 біт інформації "просигналізував чи ні", над яким визначені операції "просигналізувати", "скинути в непросігналізірованное стан" і "чекати". Очікування на просигналізувати подію є відсутність операції з негайним продовженням виконання потоку. Очікування на непросігналізірованном подію призводить до припинення виконання потоку до тих пір, поки інший потік (або ж друга фаза обробника переривання в ядрі ОС) не просигналізує подія. Можливо очікування декількох подій в режимах "будь-якого" або "всіх". Можливо також створення події, автоматично скидається в непросігналізірованное стан після пробудження першого ж - і єдиного - чекаючого потоку (такий об'єкт використовується як основа для реалізації об'єкта "критична секція"). Активно використовуються в MS Windows, як в режимі користувача, так і в режимі ядра. Аналогічний об'єкт мається і в ядрі Linux під назвою kwait_queue.
  • Критичні секції забезпечують синхронізацію подібно мьютекс за винятком того, що об'єкти, що представляють критичні секції, доступні в межах одного процесу. Події, мьютекс і семафори також можна використовувати в однопроцессного додатку, проте реалізації критичних секцій в деяких ОС (наприклад, Windows NT) забезпечують більш швидкий і більш ефективний [1] [2] механізм взаємно-виключає синхронізації - операції "отримати" і "звільнити "на критичній секції оптимізовані для випадку єдиного потоку (відсутності конкуренції) з метою уникнути будь-яких провідних в ядро ​​ОС системних викликів. Подібно мьютекс об'єкт, що представляє критичну секцію, може використовуватися тільки одним потоком в даний момент часу, що робить їх вкрай корисними при розмежуванні доступу до загальних ресурсів.
  • Умовні змінні (condvars). Подібні з подіями, але не є об'єктами, що займають пам'ять - використовується тільки адресу змінної, поняття "вміст змінної" не існує, в якості умовної змінної може використовуватися адресу довільного об'єкта. На відміну від подій, установка умовної змінної в просигналізувати стан не тягне за собою ніяких наслідків у разі, якщо на даний момент немає потоків, які очікують на змінній. Установка події в аналогічному випадку тягне за собою запам'ятовування стану "просигналізував" всередині самої події, після чого наступні потоки, бажаючі очікувати події, продовжують виконання негайно без зупинки. Для повноцінного використання такого об'єкта необхідна також операція "звільнити mutex і чекати умовну змінну атомарно". Активно використовуються в UNIX -подібних ОС. Дискусії про переваги і недоліки подій і умовних змінних є помітною частиною дискусій про переваги і недоліки Windows і UNIX.
  • Порт завершення вводу-виводу (IO completion port, IOCP). Реалізований в ядрі ОС і доступний через системні виклики об'єкт "черга" з операціями "помістити структуру в хвіст черги" і "взяти наступну структуру з голови черги" - останній виклик зупиняє виконання потоку в разі, якщо чергу порожня, і до тих пір, поки інший потік не здійснить виклик "помістити". Найважливішою особливістю IOCP є те, що структури в нього можуть поміщатися не лише явним системним викликом з режиму користувача, але і неявно всередині ядра ОС як результат завершення асинхронної операції вводу-виводу на одному з дескрипторів файлів. Для досягнення такого ефекту необхідно використовувати системний виклик "зв'язати дескриптор файлу з IOCP". У цьому випадку поміщена в чергу структура містить в собі код помилки операції вводу-виводу, а також, для випадку успіху цієї операції - число реально введених або виведених байтів. Реалізація порту завершення також обмежує число потоків, що виконуються на одному процесорі / ядрі після отримання структури з черги. Об'єкт специфічний для MS Windows, і дозволяє обробку вхідних запитів з'єднання і порцій даних у серверному програмному забезпеченні в архітектурі, де число потоків може бути менше числа клієнтів (немає вимоги створювати окремий потік з витратами ресурсів на нього для кожного нового клієнта).
  • ERESOURCE. Мьютекс, що підтримує рекурсивний захоплення, з семантикою розділяється або ексклюзивного захоплення. Семантика: об'єкт може бути або вільний, або захоплений довільним числом потоків поділюваним чином, або захоплений всього одним потоком ексклюзивним чином. Будь-які спроби здійснити захоплення, що порушує це правило, призводять до блокування потоку до тих пір, поки об'єкт не звільниться так, щоб зробити захоплення дозволеним. Також є операції виду TryToAcquire - ніколи не блокує потік, або захоплює, або (якщо потрібна блокування) повертає FALSE, нічого не роблячи. Використовується в ядрі Windows, особливо в файлових системах - так, наприклад, будь-якому кимось відкритого дискового файлу відповідає структура FCB, в якій є 2 таких об'єкта для синхронізації доступу до розміру файлу. Один з них - paging IO resource - захоплюється ексклюзивно тільки в дорозі обрізання файлу, і гарантує, що в момент обрізання на файлі немає активного введення-виведення від кеша і від відображення в пам'ять.
  • Rundown protection. Полудокументірованний (виклики присутні в файлах-заголовках, але відсутні в документації) об'єкт в ядрі Windows. Лічильник з операціями "збільшити", "зменшити" і "чекати". Очікування блокує потік до тих пір, поки операції зменшення не зменшать лічильник до нуля. Крім того, операція збільшення може відмовити, і наявність активного в даний момент часу очікування змушує відмовляти всі операції збільшення.

3. Критика термінології

Переклад англійського терміна thread як "потік" в контексті, пов'язаному із програмуванням, суперечить його ж переводу "нитка" у загальномовному контексті, а також створює колізії з терміном stream ("потік").

Однак, термін "потік" пов'язаний з перекладами іноземної технічної літератури, виконаними в 1970-х роках видавництвом "Світ". В даний час в "академічних колах" (тобто в підручниках, методичних посібниках, курсах вузів, дисертаціях і пр.) він вважається еталонним. Терміни ж "нитку", "тред" і т. п. вважаються технічними жаргонізмами.