User:Jh7H7h7dhHdkg/sandbox

ВСТУП
Зовсім недавно комп'ютери мали лише один процесор, і могли виконувати тільки одну програму одночасно. Пізніше з'явилася багатозадачність, що означало, що комп'ютери можуть виконувати кілька програм одночасно, але це не була "реальна" багатозадачність. Єдиний процесор був розділений між програмами. Операційна система переключається між програмами, які виконуються, і таким чином досягалась багатозадачність на одному процесорі.

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

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

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

Якщо потік читає комірку пам'яті, а інший потік пише в неї, то яке значення буде в комірці памяті в кінцевому результаті? Старе значення? Значення, записанедругим потоком? Або значення, яке є сумішшю старого та нового?

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

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


 * Більш ефективне використання ресурсів.
 * В деяких ситуаціях простіший дизайн програми
 * Більше гнучкі програми.

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

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

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

Перемикання контексту не є дешевим, тому перемикання між потоками повинно бути не більше необхідного.

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

МОДЕЛІ ПАРАЛЕЛІЗМУ
Паралельні системи можуть бути реалізовані з використанням різних моделей паралелізму. Модель паралелізму визначає, як потоки в системі співпрацюють для виконання завдання, яке було їм присвоєно. Різні моделі паралелізму розділяють роботу по-різному, і потоки можуть спілкуватися і співпрацювати між собою різними способами.

Схожість моделей паралелізму до розподілених систем
У паралельній системі різні потоки взаємодіють один з одним. У розподіленої системі різні процеси взаємодіють один з одним (можливо на різних комп'ютерах). Потоки та процеси дуже схожі один на одного за своєю природою. Ось чому різні моделі паралелізму часто схожі на різні архітектури розподілених систем.

Звичайно розподілені системи мають свої проблеми, наприклад, падіння мережі або віддаленого процесу і т.д. Але паралельна система, яка працює на великому сервері може стикнутись із подібними проблемами: відмова CPU, мережевої картки, диску тощо. Ймовірність відмови є низькою, але теоретично це може статися.

Оскільки моделі паралелізму аналогічні архітектурам розподілених систем, вони можуть запозичувати ідеї один від одного. Наприклад, моделі розподілу роботи серед потоків-робітників часто схожі на моделей балансування навантаження в розподілених системах. Те ж саме можна сказати і про методи обробки помилок, як логування, відмовостійкість, ідемпотентність задач тощо.

Модель паралелізму Parallel Workers
Перша модель паралелізму - паралельні потоки-робіники. Вхідні задачі відносяться до різних потоків-працівників.

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

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

Ця модель часто використувувана модель паралелізму в Java-додатках (хоча це і змінюється). Багато утиліт паралелізму в пакеті java.util.concurrent спроектовані для роботи з даною моделлю.

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

Недоілки моделі Parallel Workers
Ця модель також має і ряд недоліків, які не є очевидні одразу.

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

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

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

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

Оскільки незмінювані структури даних є елегантним рішенням для паралельного внесення змін в розділювану пам'ять, незмінні структури даних, як правило, не виконуються добре.

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

Такий список реалізований у вигляді зв'язаного списку. На жаль, зв'язані списки не дуже добре працюють на сучасному обладнанні. Кожен елемент в списку являє собою окремий об'єкт, і ці об'єкти можуть бути розкидані по всій пам'яті комп'ютера. Сучасні процесори набагато швидше доступаються до послідованих даних, так і на сучасному обладнанні ви отримаєте більшу продуктивність, якщо реалізуєте список на масиві. Масив зберігає дані послідовано. CPU може завантажити великі шматки масиву в кеш за один раз, і мати швидкий доступ до даних безпосередньо з кеша процесора. Таке здійснити неможливо зі зв'язним список, де елементи розкидані по всьому ОЗУ.

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

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

Впорядкування завдань є недетермінованим
Ще одним недоліком моделі з паралельними потоками є те, що порядок виконання роботи є недетермінованим. Немає способу, щоб гарантувати, які задачі виконані першим або останнім. Завдання А може бути дано потоку до задачі B, а виконатись пізніше ніж B.

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

Модель паралелізму Assembly Line
Друга модель паралелізму - модель паралелізму конвеєра.

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

Кожен потік-робочий не працює у своєму власному потоці, і не розділяє стану з іншими потоками. Це також можна назвати паралелізмом без стану.

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

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

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

Є ряд реалізацій reactive/event driven платформ, які все більше і більше набирають популярність. Найбільш популярні з них:


 * Vert.x
 * Akka
 * Node.JS (JavaScript)

Актори і канали
Актори і канали - два подібні приклади моделі конвеєра.

У моделі акторів кожен потік називається актором. Актори можуть відправляти повідомлення безпосередньо один одниму. Повідомлення відправляються і обробляються асинхронно. Актори можуть бути використані для реалізації одного або декількох конвеєрів, як описано вище. У моделі каналів, потоки не спілкуватися один з одним безпосередньо. Замість цього вони публікують свої повідомлення (події) на різних каналах. Інші потоки можуть потім прослуховувати повідомлення на цих каналах. Модель каналів є більш гнучкою. Потіку не потрібно знати про те, які інші потоки будуть обробляти роботу пізніше в конвеєрі. Необхідно лише знати, як відіслати роботу в канал (або надіслати повідомлення тощо). Слухачі на каналах можуть підписатися або відписатись без шкоди для потоків, які публікують завдання.

Переваги моделі Assembly Line
Модель паралелізму конвеєра має ряд переваг в порівнянні з моделлю паралельних потоків.

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

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

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

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

Можливість впорядковувати завдання
Стає можливим реалізувати паралельну систему залежно від моделі паралелізму конвеєра таким чином, що гарантує порядок виконання задач. Впорядкування задач робить систему набагато простішою з точки зору міркування про стан системи в будь-який момент часу. Крім того, ви могли б логувати всі завдання в журнал. Цей журнал можна було б використовувати, щоб відновити стан системи з нуля в разі будь-якої збою. Завдання записуються в журнал в певному порядку, і цей порядок стає гарантованим.

Недоліки моделі Assembly Line
Основним недоліком моделі паралелізму конвеєра є те, що виконання завдання часто поширюється на декілька потоків, і, таким чином, завдання розбивається на декілька класів в проекті. Таким чином, стає важче бачити, який код для якого завдання виконується.

Ця модель також може бути складнішою при писанні коду. Код іноді пишеться як обробники зворотного виклику. Маючи код з багатьма вкладених обробників зворотного виклику може призвести до такого явища як callback hell. Callback hell означає, що важко відстежити, який код виконується насправді, а також, чи кожен callback має доступ до даних, яких він потребує.

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

Функціоналний паралелізм
Функційний паралелізм - третя модель паралелізму, про яку багато говорять в даний час.

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

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

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

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