Акторська модель: Чому Erlang / OTP особливі? Чи можете ви використовувати іншу мову?


78

Я вивчав вивчення Erlang / OTP і, як результат, читав (добре, скіммінг) про модель актора.

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

Це здається досить тривіальним для реалізації на C ++ або будь-якій іншій мові:

class BaseActor {
    std::queue<BaseMessage*> messages;
    CriticalSection messagecs;
    BaseMessage* Pop();
public:
    void Push(BaseMessage* message)
    {
        auto scopedlock = messagecs.AquireScopedLock();
        messagecs.push(message);
    }
    virtual void ActorFn() = 0;
    virtual ~BaseActor() {} = 0;
}

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

Зараз я розумію, що мені не вистачає, а точніше, зациклювання на одному важливому питанні, а саме: відсутність поступливості означає, що один актор може несправедливо витрачати надмірно багато часу. Але чи є крос-платформні програми головним, що ускладнює це в C ++? (Windows, наприклад, має волокна.)

Хоча мені чогось ще не вистачає, чи модель насправді така очевидна?


5
Призначення мови програмування - допомогти у вираженні ідеї чи специфікації. Модель актора імпліцитна в Erlang, тому, хоча ви можете висловлювати свої ідеї в моделі будь-якою мовою, в Erlang буде набагато краще, тому що панель котлів зроблена за вас.
GManNickG

3
@GMan як тільки котлова плита буде зроблена (це було б одноразово подумати, я б подумав) у чому перевага?
Сет Карнегі,

4
@SethCarnegie: Це справді суть мого запитання.
Джонатан Вінкс підморгує

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

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

Відповіді:


87

Код С ++ не стосується справедливості, ізоляції, виявлення несправностей або розподілу, що все те, що Erlang пропонує як частину своєї моделі актора.

  • Жоден актор не має права голодувати будь-якого іншого актора (справедливість)
  • Якщо один актор зазнає аварії, це повинно впливати лише на нього (ізоляція)
  • Якщо один актор зазнає аварії, інші актори повинні мати можливість виявити та реагувати на цей збій (виявлення несправності)
  • Актори повинні мати можливість спілкуватися по мережі так, ніби вони перебувають на одній машині (розподіл)

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

Крім того, усі бібліотеки та інструменти, написані на Ерлангі, можуть припустити, що так працює світ і бути спроектованими відповідно.

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

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


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

5
Усі перелічені вами властивості надаються операційною системою процесам. Програми на C ++ можуть легко ними скористатися, як і будь-яка інша програма. Я думаю, що ключовим для Erlang є те, що його актори набагато дешевші, ніж процеси ОС для надання цих властивостей. Як результат, акторів можна використовувати більш вільно.
Кармастан

3
@Karmastan Так, процеси Erlang дуже дешеві, оскільки / так що паралельність є основною абстракцією структурування додатків. Ми вважаємо за краще називати їх процесами не акторами, ми не чули про акторів, коли розробляли Ерланг. :-)
rvirding

32

Я не люблю цитувати себе, але з першого правила програмування Вірдінга

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

Щодо Грінспуна. Подібне правило має і Джо (Армстронг).

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

Захоплюючись, я зупинюсь тут.


"так що я
serv-inc

22

Це насправді відмінне запитання, і воно отримало чудові відповіді, які, можливо, ще не переконливі.

Щоб додати відтінку та акценту до інших чудових відповідей, вже тут, розгляньте, що забирає Ерланг (порівняно з традиційними мовами загального призначення, такими як C / C ++), щоб досягти відмовостійкості та тривалості роботи.

По-перше, це забирає замки. У книзі Джо Армстронга викладено цей мислительний експеримент: припустимо, ваш процес набуває блокування, а потім негайно збивається (збій пам'яті призводить до збою процесу, або живлення відмовляє частину системи). Наступного разу, коли процес буде чекати того самого блокування, система просто зайшла в глухий кут. Це може бути очевидним блокуванням, як у виклику AquireScopedLock () у зразковому коді; або це може бути неявний замок, придбаний від вашого імені менеджером пам'яті, скажімо при виклику malloc () або free ().

У будь-якому випадку, ваш збій процесу зупинив прогрес всієї системи. Фіні. Кінець історії. Ваша система мертва. Якщо ви не гарантуєте, що кожна бібліотека, яку ви використовуєте в C / C ++, ніколи не викликає malloc і ніколи не отримує блокування, ваша система не витримує несправностей. Системи Erlang можуть і вбивають процеси за власним бажанням, коли перебувають під великим навантаженням, щоб досягти прогресу, тому ваші масштаби Erlang-процесів повинні бути вбиті (у будь-якій окремій точці виконання), щоб підтримувати пропускну здатність.

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

По-друге, Erlang забирає статичну типізацію, що, у свою чергу, дозволяє гарячу заміну коду та запуск двох версій одного і того ж коду одночасно. Це означає, що ви можете оновити свій код під час виконання, не зупиняючи систему. Це те, як системи залишаються в курсі дев'яти 9 або 32 мсек простою / рік. Їх просто модернізують на місці. Щоб оновити ваші функції С ++ доведеться перевстановити вручну, а одночасне виконання двох версій не підтримується. Оновлення коду вимагає простою системи, і якщо у вас великий кластер, який не може запустити більше однієї версії коду одночасно, вам доведеться зняти весь кластер одночасно. Ой А у телекомунікаційному світі це не терпимо.

Крім того, Ерланг забирає спільну пам'ять і спільний збір сміття; кожен легкий процес - це сміття, що збирається самостійно. Це просте розширення першого пункту, але підкреслюється, що для справжньої відмовостійкості потрібні процеси, які не взаємозв’язані з точки зору залежностей. Це означає, що ваші паузи в GC порівняно з Java є терпимими (малі, замість того, щоб робити паузу на півгодини для завершення GC на 8 ГБ) для великих систем.


1
По-перше, ви можете використовувати lock_guard, який звільнить ваш замок на випадок аварійного завершення роботи програми. По-друге, ви можете впровадити систему гарячої заміни в C ++, але це болісно в ... Проблема з паралельністю полягає в тому, що примітиви синхронізації, навіть атоми, вводять огорожі та бар'єри пам'яті і сповільнюють. Чим більше у вас ниток, тим більше ви будете гальмувати. Erlang, як clojure або haskell, не використовує мьютекси або атоми, що змушує розробника по-іншому викласти проблему. Це дуже ефективний спосіб вирішення проблем паралельності
Асьє Гутьєррес

Звучить вірно, але це лише порівняння C ++, і C ++ завжди легко звинуватити. Чи не можливо це мати, наприклад, на Java (або Clojure)? Блокування в Java безпечні, і є способи компіляції / завантаження коду під час виконання (у Clojure це також дуже просто).
Відображуване ім’я


3

Набагато менше йдеться про модель актора і набагато більше про те, як важко правильно написати щось аналогічне OTP в C ++. Крім того, різні операційні системи забезпечують кардинально різні налагодження та системні засоби, а віртуальна машина Ерланга та декілька мовних конструкцій підтримують єдиний спосіб з'ясувати, що саме роблять усі ці процеси, які було б дуже важко зробити рівномірним способом (або, можливо, зробити на декількох платформах. (Важливо пам’ятати, що Erlang / OTP передує поточному шуму щодо терміна "модель актора", тому в деяких випадках подібні дискусії порівнюють яблука та птеродактилі; чудові ідеї схильні до незалежних винаходів.)

Все це означає, що, хоча ви, звичайно, можете написати набір програм "актор" на іншій мові (я знаю, я давно це робив на Python, C та Guile, не усвідомлюючи цього до того, як зіткнувся з Ерлангом, включаючи форму монітори та посилання, і до того, як я коли-небудь почув термін "модель актора"), розуміння того, як насправді виникають процеси вашого коду і що відбувається серед нихнадзвичайно важко. Erlang застосовує правила, які ОС просто не може без капітального ремонту ядра - капітального ремонту ядра, який, мабуть, не був би корисним у цілому. Ці правила виявляються як загальними обмеженнями для програміста (які завжди можна отримати, якщо вам це дійсно потрібно), так і основними обіцянками, які гарантує система для програміста (які можуть бути навмисно порушені, якщо вам це дійсно потрібно).

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

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

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

Отже, насправді не йдеться про потребу в мові Erlang, а про час роботи та вже існуючий OTP, що виражається досить чисто, а реалізація будь-чого близького до нього іншою мовою є надзвичайно складною. OTP - це просто важкий вчинок. У тому ж ключі нам також не потрібен С ++, ми могли б просто дотримуватися необробленого двійкового введення, Brainfuck і розглянути Assembler нашою мовою високого рівня. Нам також не потрібні поїзди або кораблі, оскільки всі ми знаємо, як ходити і плавати.

Все сказане, байт-код віртуальної машини добре задокументований, і з’явилося ряд альтернативних мов, які компілюються до нього або працюють із середовищем виконання Erlang. Якщо ми розділимо запитання на мовну / синтаксичну частину ("Чи повинен я розуміти Місячні руни, щоб робити паралелізм?"), І частину платформи ("Чи є OTP найзрілішим способом досягнення паралелізму, і чи допоможе він мені в цьому найскладніше , найпоширеніші підводні камені, які можна знайти в паралельному розподіленому середовищі? "), тоді відповідь (" ні "," так ").


2

Касабланка - ще одна нова дитина в блоці акторів. Типовий асинхронний акцепт виглядає так:

PID replyTo;
NameQuery request;
accept_request().then([=](std::tuple<NameQuery,PID> request)
{
   if (std::get<0>(request) == FirstName)
       std::get<1>(request).send("Niklas");
   else
       std::get<1>(request).send("Gustafsson");
}

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

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