Основні принципи взаємодії потоків



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

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

Усі інші потоки є такими, що взаємодіють. Ці потоки мають дані, спільні з іншими потоками. Їх виконання залежить не тільки від вхідних даних, але й від виконання інших потоків, тобто є недетермінованими.

Результати виконання незалежного потоку можна повторити, чого не можна сказати про потоки, що взаємодіють.

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

Обійтися без реалізації взаємодії потоків неможливо з кількох причин.

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

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

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

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

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

Основні проблеми взаємодії потоків

Проблема змагання

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

total_amount = total_amount + new_amount;

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

Насправді цей найпростіший оператор зводиться до послідовності таких дій:

· отримання поточного значення total_amount із глобальної пам’яті;

· збільшення його на new_amount і збереження результату в глобальній пам’яті.

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

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

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

Розглянемо основні підходи до розв’язання проблеми змагань.

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

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

У всіх інших випадках потрібно забезпечувати захист змін від впливу інших потоків. Це і є основним завданням синхронізації.


Дата добавления: 2018-04-05; просмотров: 1043; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!