Прихильники ПС заявляють, що одночасність легко, оскільки їх парадигма уникає змінних станів. Я не розумію.
Я хотів виступити з цим загальним питанням як хтось функціональний неофіт, але протягом багатьох років мої очні яблука були побічними ефектами і хотіли б пом'якшити їх з будь-яких причин, включаючи легші (або конкретно "безпечніші, менша схильність до помилок "). Коли я переглядаю своїх функціональних однолітків і те, що вони роблять, трава здається трохи зеленішою і пахне приємніше, принаймні в цьому плані.
Серійні алгоритми
Це стосується вашого конкретного прикладу, якщо ваша проблема має серійний характер і B не може бути виконана, поки A не буде закінчено, концептуально ви не можете паралельно запускати A і B незалежно від того. Ви повинні знайти спосіб розірвати залежність порядку, як у вашій відповіді, спираючись на паралельні рухи, використовуючи старий стан гри, або використовувати структуру даних, яка дозволяє окремо змінювати її частини для усунення залежності порядку, запропонованої в інших відповідях або щось подібне. Але, безумовно, існує частка таких концептуальних дизайнерських проблем, коли не обов'язково просто так багатопрочитати все, тому що речі незмінні. Деякі речі просто матимуть серійний характер, поки ви не знайдете якийсь розумний спосіб подолати залежність замовлення, якщо це навіть можливо.
Легша паралельність
Однак, є багато випадків, коли нам не вдається паралелізувати програми, які передбачають побічні ефекти в місцях, які потенційно можуть значно підвищити продуктивність просто через можливість того, що він може не бути безпечним для потоків. Один із випадків, коли усунення стану, що змінюється (або, конкретніше, зовнішні побічні ефекти) дуже допомагає, оскільки я бачу, це те, що він перетворюється "може або не може бути безпечним для потоків" у "безумовно безпечний для потоків" .
Щоб зробити це твердження більш конкретним, врахуйте, що я даю вам завдання реалізувати функцію сортування в C, яка приймає компаратор і використовує його для сортування масиву елементів. Це повинно бути досить узагальненим, але я дам вам легке припущення, що він буде використаний проти входів такого масштабу (мільйонів елементів і більше), що, безсумнівно, буде корисним завжди використовувати багатопотокову реалізацію. Чи можете ви багатопотоково читати свою функцію сортування?
Проблема полягає в тому, що ви не можете , тому що компаратори ваших сортування викликів функцій можутьвикликати побічні ефекти, якщо ви не знаєте, як вони реалізовані (або принаймні задокументовані) для всіх можливих випадків, що неможливо без дегенералізації функції. Компаратор може зробити щось огидне, наприклад, змінити глобальну змінну всередині неатомним способом. 99,9999% компараторів цього не можуть зробити, але ми все ще не можемо багатократно прочитати цю узагальнену функцію просто через 0,00001% випадків, які можуть викликати побічні ефекти. Як результат, можливо, вам доведеться запропонувати як однопоточну, так і багатопотокову функцію сортування та передати відповідальність програмістам, які використовують її, щоб вирішити, яку саме використовувати на основі безпеки потоку. І люди все ще можуть використовувати однопоточну версію і пропускають можливості багатопотокового перегляду, тому що вони також можуть бути не впевнені, чи порівняльник безпечний для потоків,
Існує ціла кількість мозкових сил, які можуть бути залучені до раціоналізації безпеки потоку речей, не викидаючи замки скрізь, які можуть піти, якщо у нас просто були жорсткі гарантії, що функції не спричинять побічних ефектів для теперішнього і майбутнього. І є страх: практичний страх, тому що кожен, кому довелося відладжувати перегоновий стан декілька занадто багато разів, певно, вагається з приводу багатопотокового читання, що вони не можуть бути на 110% впевнені, що це безпечно для потоків і залишатиметься таким. Навіть для самого параноїка (з якого я, мабуть, принаймні прикордонний), чиста функція забезпечує те почуття полегшення та впевненості, що ми можемо сміливо називати це паралельно.
І це один з основних випадків, коли я вважаю це настільки вигідним, якщо ви можете отримати жорстку гарантію того, що такі функції є безпечними для потоків, які ви отримуєте з чисто функціональних мов. Інша полягає в тому, що функціональні мови часто сприяють створенню функцій, позбавлених в першу чергу побічних ефектів. Наприклад, вони можуть надати вам стійкі структури даних, де досить ефективно ввести масивну структуру даних, а потім вивести абсолютно нову, лише незначна її частина зміниться з оригіналу, не торкаючись оригіналу. Ті, хто працює без таких структур даних, можуть захотіти змінити їх безпосередньо і втратити деяку безпеку потоку на цьому шляху.
Побічні ефекти
Однак, я не погоджуюся з однією частиною з усією повагою до своїх функціональних друзів (які я вважаю супер крутими):
[...] тому що їх парадигма уникає змінних станів.
Не обов'язково незмінність робить паралельність настільки практичною, як я це бачу. Це функції, які уникають побічних ефектів. Якщо функція вводить масив для сортування, копіювання, а потім мутує копію для сортування її вмісту та виводить копію, вона все одно настільки ж безпечна для потоків, як і робота з деяким незмінним типом масиву, навіть якщо ви передаєте той же вхід масив до нього з декількох потоків. Тому я думаю, що все ще є місце для змінних типів для створення дуже сумісного коду, так би мовити, хоча є багато додаткових переваг для непорушних типів, включаючи стійкі структури даних, які я використовую не стільки для їх незмінних властивостей, скільки для виключити витрату на те, щоб все глибоко копіювати, щоб створити функції без побічних ефектів.
І часто виникають витрати на те, щоб зробити функції без побічних ефектів у вигляді перемішування та копіювання додаткових даних, можливо, додатковий рівень непрямості та, можливо, деякий GC на частинах стійкої структури даних, але я дивлюся на одного свого приятеля, який має 32-ядерна машина, і я думаю, що обмін, мабуть, того вартий, якщо ми можемо впевненіше робити більше справ паралельно.