Model aktora: dlaczego erlang jest wyjątkowy? Albo po co ci inny język?

Zajrzałem do nauki Erlanga i w rezultacie przeczytałem (ok, skimming) o modelu aktora.

Z tego, co rozumiem, model aktora jest po prostu zestawem funkcji (uruchamianych w lekkich wątkach zwanych "procesami" w erlangu), które komunikują się ze sobą tylko poprzez przekazywanie wiadomości.

Wydaje się to dość trywialne, aby zaimplementować w C++, lub innym języku:

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;
}

Z każdym z procesów jest instancją pochodnej Jednostki Bazowej. Aktorzy komunikują się ze sobą tylko poprzez przekazywanie wiadomości. (mianowicie pchanie). Aktorzy rejestrują się za pomocą centralnej mapy inicjalizacji, która pozwala innym aktorom je znaleźć i umożliwia centralną funkcję przez nie przebiegającą.

Rozumiem, że brakuje mi tutaj, a raczej jednej ważnej kwestii, a mianowicie: brak plonowania oznacza, że pojedynczy aktor może niesprawiedliwie zużywać nadmierny czas. Ale czy wieloplatformowe koroutines są podstawową rzeczą, która sprawia, że to trudne w C++? (Windows na przykład ma włókna.)

Czy coś jeszcze mi umyka, czy model naprawdę jest taki oczywisty?

Na pewno nie próbuję tu rozpętać wojny ognia, chcę tylko zrozumieć, czego mi brakuje, ponieważ jest to zasadniczo to, co już robię, aby móc nieco rozumować o współbieżnym kodzie.

Author: Jonathan Winks, 2011-11-13

6 answers

Kod C++ nie zajmuje się sprawiedliwością, izolacją, wykrywaniem błędów lub dystrybucją, które są wszystkim, co Erlang wnosi w ramach swojego modelu aktora.

  • żaden aktor nie może zagłodzić żadnego innego aktora (uczciwość)
  • Jeśli jeden z aktorów ulegnie awarii, powinno to mieć wpływ tylko na tego aktora (izolacja)
  • Jeśli jeden z aktorów ulegnie awarii, inni aktorzy powinni być w stanie wykryć i zareagować na tę awarię (wykrywanie usterek)]} Aktorzy powinni być w stanie komunikować się przez sieć tak, jakby były na tej samej maszynie (Dystrybucja)

Również emulator Beam SMP wprowadza harmonogramowanie JIT aktorów, przenosząc je do rdzenia, który jest w tej chwili najmniej utylizowanym, a także hibernuje wątki na niektórych rdzeniach, jeśli nie są już potrzebne.

Ponadto wszystkie biblioteki i narzędzia napisane w Erlangu mogą zakładać, że tak działa świat i być odpowiednio zaprojektowane.

Te rzeczy nie są niemożliwe do zrobienia w C++, ale dostają coraz trudniej dodać fakt, że Erlang działa na prawie wszystkich głównych konfiguracjach sprzętu i systemu operacyjnego.

Edit: Just found a description by Ulf Wiger about what he sees Erlang style concurrency as.

 82
Author: Lukas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-15 15:41:03

Nie lubię cytować siebie, ale z Pierwsza zasada Virdinga programowania

Każdy wystarczająco skomplikowany program współbieżny w innym języku zawiera ad hoc informacyjnie określoną, powolną implementację połowy Erlanga.

W odniesieniu do Greenspun. Joe (Armstrong) ma podobną zasadę.

Problem polega na tym, aby nie wdrażać aktorów, to nie jest takie trudne. Problem polega na tym, aby wszystko działało razem: procesy, komunikacja, usuwanie śmieci, prymitywy językowe, obsługa błędów itp ... Na przykład używanie wątków OS skaluje się źle, więc musisz to zrobić sam. To byłoby jak próba "sprzedania" języka OO, w którym można mieć tylko 1K obiektów i są one ciężkie do tworzenia i używania. Z naszego punktu widzenia współbieżność jest podstawową abstrakcją dla strukturyzowania aplikacji.

Daje się ponieść, więc zatrzymam się tutaj.

 29
Author: rvirding,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-13 21:59:25

Jest to rzeczywiście doskonałe pytanie i otrzymał doskonałe odpowiedzi, które być może są jeszcze nieprzekonujące.

Aby dodać cień i nacisk na inne świetne odpowiedzi już tutaj, zastanów się, co Erlang zabiera (w porównaniu do tradycyjnych języków ogólnego przeznaczenia, takich jak C / C++) w celu uzyskania tolerancji błędów i czasu pracy.

Po pierwsze, usuwa zamki. Książka Joe Armstronga przedstawia ten eksperyment myślowy: przypuśćmy, że twój proces nabiera zamka, a następnie natychmiast ulega awarii (usterka pamięci powoduje awarię procesu lub zasilanie nie wchodzi w skład systemu). Następnym razem, gdy proces czeka na tę samą blokadę, system jest zablokowany. Może to być oczywista blokada, jak w wywołaniu AquireScopedLock () w przykładowym kodzie; lub może to być ukryta blokada uzyskana w Twoim imieniu przez menedżera pamięci, na przykład podczas wywoływania malloc () lub free().

W każdym razie, twoja awaria procesu zatrzymała cały system przed postępem. Fini. Koniec historii. Twój system jest martwy. Jeśli nie możesz zagwarantować, że każda biblioteka, której używasz w C/C++, nigdy nie wywoła malloc i nigdy nie uzyska blokady, Twój system nie jest odporny na błędy. Systemy Erlang mogą i wykonują kill procesów do woli, gdy pod dużym obciążeniem w celu dokonania postępu, więc w skali procesy Erlang muszą być killable (w dowolnym punkcie wykonania) w celu utrzymania przepustowości.

Istnieje częściowe obejście: używanie leasingu Wszędzie zamiast zamków, ale nie masz upewnij się, że wszystkie biblioteki, z których korzystasz, również to robią. A logika i rozumowanie o poprawności robi się naprawdę Owłosione szybko. Co więcej, dzierżawy odzyskują się powoli (po upływie limitu czasu), więc cały system po prostu stał się naprawdę powolny w obliczu awarii.

Po drugie, Erlang usuwa statyczne typowanie, co z kolei umożliwia zamianę kodu na gorąco i jednoczesne uruchamianie dwóch wersji tego samego kodu. Oznacza to, że można uaktualnić kod w czasie wykonywania bez zatrzymywania systemu. Oto jak systemy pozostają sprawne przez dziewięć 9 lub 32 msec przestojów / rok. Są one po prostu ulepszane na miejscu. Twoje funkcje C++ będą musiały zostać ręcznie połączone, aby zostać zaktualizowane, a uruchamianie dwóch wersji w tym samym czasie nie jest obsługiwane. Uaktualnienia kodu wymagają przestoju systemu, a jeśli masz duży klaster, który nie może uruchomić więcej niż jednej wersji kodu na raz, musisz usunąć cały klaster na raz. AUĆ. A w świecie telekomunikacji nie do zniesienia.

DODATKOWO Erlang zabiera współdzielona pamięć i współdzielone zbieranie śmieci; każdy lekki Proces jest śmieciami zbieranymi niezależnie. Jest to proste rozszerzenie pierwszego punktu, ale podkreśla, że dla prawdziwej tolerancji błędów potrzebne są procesy, które nie są ze sobą powiązane pod względem zależności. Oznacza to, że pauzy GC w porównaniu z Javą są tolerowane (małe zamiast pauzować pół godziny na 8GB GC do zakończenia) dla dużych systemów.

 21
Author: jaten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-21 18:29:38

Istnieją biblioteki actor dla C++:

I lista niektórych bibliotek dla innych języków.

 14
Author: Alexey Romanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-20 05:44:42

O wiele mniej chodzi o model aktora, a o wiele więcej o to, jak trudno jest poprawnie napisać coś analogicznego do OTP w C++. Ponadto różne systemy operacyjne zapewniają radykalnie różne debugowanie i Narzędzia systemowe, a maszyny wirtualne Erlanga i kilka konstrukcji językowych wspierają Jednolity sposób określania, do czego służą wszystkie te procesy, co byłoby bardzo trudne do zrobienia w jednolity sposób (a może w ogóle) na kilku platformach. (Ważne jest, aby pamiętać, że Erlang / OTP przed obecnym szumem termin "model aktora", więc w niektórych przypadkach tego typu dyskusje porównują jabłka i pterodaktyle; wielkie pomysły są podatne na samodzielne wynalazki.)

Wszystko to oznacza, że chociaż z pewnością można napisać pakiet programów "model aktora" w innym języku (wiem, robiłem to przez długi czas w Pythonie, C i Guile, nie zdając sobie z tego sprawy, zanim spotkałem Erlanga, w tym formę monitorów i linków, i zanim kiedykolwiek słyszałem termin " aktor model"), zrozumienie, w jaki sposób procesuje się Twój kod i co się między nimi dzieje jest niezwykle trudne. Erlang wymusza reguły, których system operacyjny po prostu nie może bez większych przeglądów jądra - przeglądów jądra, które prawdopodobnie nie byłyby korzystne ogólnie. Zasady te przejawiają się zarówno jako Ogólne Ograniczenia dla programisty (które zawsze można obejść, jeśli naprawdę trzeba), jak i podstawowe obietnice systemowych gwarancji dla programisty (które mogą być celowo złamane, jeśli naprawdę trzeba również).

Na przykład wymusza, że dwa procesy nie mogą współdzielić stanu, aby chronić Cię przed skutkami ubocznymi. Nie oznacza to, że każda funkcja musi być "czysta" w tym sensie, że wszystko jest referencyjnie przejrzyste (oczywiście nie, chociaż uczynienie tyle Twojego programu referencyjnie przejrzystym, co praktyczne, jest jasnym celem projektu większości projektów Erlang), ale raczej, że dwa procesy Nie stale tworzą warunki rasowe związane ze wspólnym stanem lub niezgodnością. (To jest bardziej to, co "skutki uboczne" oznacza w kontekście Erlang, nawiasem mówiąc; wiedząc, że może pomóc rozszyfrować niektóre z dyskusji kwestionujących, czy Erlang jest "naprawdę funkcjonalny, czy nie" w porównaniu z Haskell lub zabawka "czyste" języki.)

Z drugiej strony Erlang runtime gwarantuje dostarczanie wiadomości. Jest to coś, czego bardzo brakuje w środowisku, w którym musisz komunikować się wyłącznie przez niezarządzane porty, rury, pamięć współdzieloną i wspólne pliki, którymi zarządza jądro systemu operacyjnego (a zarządzanie jądrem systemu operacyjnego tymi Zasobami jest koniecznie bardzo minimalne w porównaniu z tym, co zapewnia środowisko uruchomieniowe Erlang). Nie oznacza to, że Erlang gwarantuje RPC (w każdym razie przekazywanie wiadomości to , NIE RPC, ani wywołanie metody!), nie obiecuje, że Twoja wiadomość jest poprawnie adresowana i nie obiecuje, że proces, do którego próbujesz wysłać wiadomość, istnieje lub jest żywy. To tylko gwarancja dostawy jeśli to, do czego wysyłasz, jest ważne w tym momencie.

Zbudowana na tej obietnicy jest obietnica, że monitory i linki są dokładne. Na tej podstawie środowisko uruchomieniowe Erlang sprawia, że cała koncepcja "klastra sieciowego" topi się, gdy zrozumiesz, co dzieje się z systemem (i jak używać erl_connect...). Pozwala to na przeskoczenie zestawu skomplikowanych przypadków współbieżności, co daje dużą przewagę nad kodowaniem dla udanej sprawy, zamiast być pogrążonym w bagnie technik obronnych potrzebnych do programowania współbieżnego.

Więc tak naprawdę nie chodzi o potrzebowanie Erlang, język, jego o środowisko uruchomieniowe i OTP już istniejące, wyrażone w dość czysty sposób, a wdrożenie wszystkiego blisko niego w innym języku jest niezwykle trudne. OTP jest po prostu trudnym aktem do naśladowania. W tym samym duchu, tak naprawdę nie potrzebujemy C++, możemy po prostu trzymać się surowego wejścia binarnego, Brainfuck i uznać assemblera za nasz język na wysokim poziomie. Nie potrzebujemy również pociągów ani statków, ponieważ wszyscy wiemy, jak chodzić i pływać.

Wszystko to powiedziawszy, bajtowy Kod maszyny Wirtualnej jest dobrze udokumentowany i pojawiło się wiele alternatywnych języków, które kompilują się do niej lub pracują z uruchomieniem Erlang. Jeśli podzielimy pytanie na Część językową/składniową ("Czy muszę rozumieć Runy księżycowe, aby wykonać współbieżność?") i część platformy ("czy OTP jest najbardziej dojrzałym sposobem na współbieżność i czy poprowadzi mnie po najtrudniejszych, najczęściej pułapki, które można znaleźć w równoległym, rozproszonym środowisku?") wtedy odpowiedź brzmi ("nie","tak").

 3
Author: zxq9,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-02 00:35:21

Casablanca to kolejny nowy dzieciak w bloku modelek. Typowa akceptacja asynchroniczna wygląda tak:

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");
}

(osobiście uważam, że CAF lepiej ukrywa dopasowanie wzorca za ładnym interfejsem.)

 2
Author: mavam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-06 14:53:25