Як пояснити, чому багатоструменевий складний


84

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

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

Тож як я можу його представити або пояснити, чому він, можливо, недооцінює складності одночасності, паралелізму та багатопоточності? А може, я помиляюся?

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


17
Багатопоточна річ проста. Правильна синхронізація є складною.
Vineet Reynolds

33
Занесіть до кімнати трьох людей, бажано з різними акцентами, і попросіть їх пояснити різні, що перекриваються частини проблеми паралельності .... одночасно.
greyfade

Багатопотокове читання може бути дуже важким або дуже простим, залежно від проблеми, що існує, і мовної підтримки. Clojure полегшує clojure.org/concurrent_programming
робота

4
@Job Паралельне програмування завжди важке (у реальних проектах), незалежно від того, якою мовою ви користуєтесь. Scala, Clojure або Erlang роблять трохи розумнішими, коли ви хочете порівнювати його з мовами, які використовують та заохочують зміни станів.
Хірон

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

Відповіді:


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

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


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

2
Amazon має ненадовго заблокувати інвентаризацію окремих предметів на одному складі під час обробки замовлення. Якщо на одному конкретному предметі раптовий, величезний пробіг, продуктивність замовлення для цього товару буде страждати, поки пропозиція не буде вичерпана і доступ до інвентаря не стане доступним лише для читання (і, отже, на 100% доступним). Одна річ, яку Amazon намагається досягти, ніж інші програми, - це можливість чергувати замовлення, поки не відбудеться повторна акція, і можливість обслуговування замовлень у черзі до того, як повторний запас стане доступним для нових замовлень.
Blrfl

@Blrfl: Програми можуть це робити, якщо вони написані, щоб використовувати повідомлення, що проходять через черги. Немає необхідності, щоб усі повідомлення на певний потік проходили через одну чергу…
Donal Fellows

4
@Donal Fellows: Якщо на одному складі є 1М віджетів, а замовлення 1М надходять в один і той же момент, всі ці запити серіалізуються на певному рівні під час узгодження позицій із замовленнями незалежно від того, яким чином вони обробляються. Практична реальність полягає в тому, що Amazon, ймовірно, ніколи не має в наявності стільки віджетів, що затримка в обробці роздачі замовлень стає неприпустимо високою до закінчення запасів, і всі інші в черзі можуть сказати (паралельно), "ми вийшли. " Черги з повідомленнями - чудовий спосіб запобігання тупикам, але вони не вирішують проблеми високої суперечки за обмежений ресурс.
Blrfl

79

Нить є простий. Кодування програми для багатопотокової передачі дуже-дуже просто.

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

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

Багато людей не використовують черги повідомлень і намагаються оновити спільні об’єкти та створювати проблеми для себе.

Що стає складним - це створити алгоритм, який добре працює при передачі даних між кількома чергами. Це важко. Але механіка існуючих потоків (через спільні черги) проста.

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

Якщо ви хочете проілюструвати проблему оновлення спільного об’єкта, це просто. Сядьте через стіл з купою паперових карток. Запишіть простий набір обчислень - 4 або 6 простих формул - з великою кількістю місця на сторінці.

Ось гра. Кожен читаєш формулу, пишеш відповідь і кладеш картку з відповіддю.

Кожен з вас зробить половину роботи, правда? Ви закінчили за половину часу, правда?

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

Існує багато, багато способів створити умови для перегонів із поганими або незаблокованими ресурсами.

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


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

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

25

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

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

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

Так що так, технічно багатоструменеві складно.
"Брязкати замком" - це, мабуть, найменш масштабоване рішення проблем одночасності, і фактично перемагає цілі багатопотокової роботи. Що потрібно зробити, це повернути проблему назад до несумісної моделі виконання. Чим більше ви це зробите, тим більше шансів на те, що у вас є лише один потік у той час (або 0 у тупику). Це перемагає цілі.
Це як би сказати: "Вирішити проблеми 3-го світу легко. Просто кинь на нього бомбу". Тільки тому, що існує тривіальне рішення, це не робить проблему тривіальною, оскільки ви дбаєте про якість результату.

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


14

Я думаю, що в цьому питанні немає технічного кута - ІМО, це питання довіри. Нас зазвичай нас просять відтворити складні програми, наприклад, - о, я не знаю - наприклад, Facebook. Я прийшов до висновку, що якщо вам доводиться пояснювати складність завдання непосвяченим / керівництву - то в Данії щось гниле.

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

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


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

6

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

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


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

Чи можна вирішити подібні проблеми з тупиком, завжди купуючи ресурси у встановленому порядку?
комман

@compman, ні. Оскільки 2 потоки можна спробувати схопити за той самий ресурс в той самий момент, і ці нитки не обов'язково потребують однакового набору ресурсів - достатньо лише перекриття, щоб викликати проблеми. Одна із схем - повернути ресурс "назад", а потім дочекатися випадкового періоду, перш ніж знову захопити його. Цей період відновлення трапляється у ряді протоколів, найпершим з яких був Алоха. en.wikipedia.org/wiki/ALOHAnet
Тангурена

1
Що робити, якщо кожен ресурс у програмі мав число, і коли потоці / процесу потрібен набір ресурсів, він завжди блокував ресурси у збільшенні числового порядку? Я не думаю, що може статися тупик.
комман

1
@compman: Це дійсно спосіб уникнути тупикової ситуації. Можна створити інструменти, які дозволяють вам автоматично перевірити це; тож якщо у вашій програмі ніколи не виявлено блокування інших ресурсів, ніж у збільшенні числового порядку, у вас ніколи не було потенційного глухого кута. (Зверніть увагу, що потенційні тупики лише коли-небудь перетворюються на справжні тупикові місця, коли ваш код працює на комп'ютері замовника).
gnasher729

3

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

Тому єдино правильне одночасне застосування - це те, що є коректно правильним, що часто не застосовується в розробці програмного забезпечення. Як результат, відповідь С.Лот є найкращою загальною порадою, оскільки передача повідомлень порівняно легко довести правильність.


3

Коротка відповідь двома словами: НАВЧАЛЬНИЙ НОНДЕТЕРМІНІЗМ

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

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

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

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

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

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


0

Мене вперше вчили, що це може спричинити проблеми, побачивши просту програму, яка запускала 2 теми і обидва друкували на консолі одночасно з 1-100. Замість:

1
1
2
2
3
3
...

Ви отримуєте щось подібне:

1
2
1
3
2
3
...

Запустіть його ще раз, і ви можете отримати абсолютно різні результати.

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


-3

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

Наведіть це на будівництво будинку.

Намагайтеся спати вночі, уявляючи, що ви архітектор. :)


-3

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

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


3
Важка частина справді важча, але навіть ця легка частина не така проста.
PeterAllenWebb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.