Axum – Agenci w akcji

Powakacyjna kontynuacja serii na temat różnych podejść w pisaniu aplikacji równoległych. Po koniec lipca przedstawiłem model Agenta, a dzisiaj chciałbym przedstawić jego Microsoftową implementację.

Axum to kolejny inkubacyjny projekt z Redmond, tak więc naprawdę nie wiadomo, co się z nim dalej stanie. Model agenta [1], który przedstawiłem w poprzednim poście, został przez giganta potraktowany całkiem poważnie. Solidna implementacja, skalowalność, wtyczka do Visual Studio – to wszystko daje całkiem duże pole do popisu, ale powoli…

Axum to jezyk programowania – pewnie się zdziwiliście – w 99% oparty na C# i w pełni współpracujący z platformą .NET. To narzędzie umożliwiające koordynację niezależnych komponentów aplikacji, definiujące agentów oraz ich interakcje. Jest to język „special-purpose”, więc z miejsca wprowadzona jest tu równoległość.

Najprościej przedstawić Axum budując bardzo prostą aplikację taką jak  kalkulator. Na początek zatem musimy zaplanować, jakie elementy będą nam potrzebne – myśląc naturalnie w świecie agentów. Otóż, potrzebny będzie nam agent wykonujący pojedyncze działanie, np. dodawanie, ale również agent, który przekaże mu dane jednocześnie odbierając je od nas (np. z konsoli), tj. agent utożsamiany z punktem wejściowym aplikacji. Agent powinien również definiować kanał, czyli określenie rodzaju wiadomości, jaką może obsłużyć.

Zacznijmy zatem od kanału. Nazwijmy go MathChannel:

  1. channel MathChannel
  2. {
  3.    input int X;
  4.    input int Y;
  5.    output int Z;
  6. }

Jak widać, kanał definiuje pewne pola zwane portami. Oprócz typu decyduje się również o kierunku. Porty to nic innego jak bufory. Kolejność używania portów przez agenta można zdefiniować w tzw. patternach, ale na tym etapie poznawania Axum nie będą one potrzebne.

Stwórzmy zatem agenta dodawania:

  1. public agent Adder : channel MathChannel
  2. {
  3.    public Adder()
  4.    {
  5.       while(true)
  6.       {
  7.          Z <– ( receive(X) + receive(Y) );
  8.       }
  9.    }
  10. }

Należy zauważyć, że definiując agenta, musimy zdecydować, jaki kanał będzie obsługiwał, a w tym wypadku oczywiście będzie to nasz kanał matematyczny. Jak widzicie, kanał nie decyduje o funkcjonalności, a jedynie o możliwym interfejsie obsługi. Konstruktor tego agenta jest punktem wejściowym. Czytanie z kanału jest blokujące, tak więc łatwo zauważyć, na czym polega działanie agenta: czeka on na parametr, który pojawi się na porcie X w kanale. Jeśli przyjdzie, czeka na Y, a jeśli i ten przyjdzie – na port Z wstawia wynik działania i znów nasłuchuje na porcie X. O ile konstrukcja pobierania z portu jest „normalna”, to wysyłanie wyniku na port operatorem „<–” może troszkę dziwić. Axum definiuje więcej podobnych operatorów.

Czas na kluczowego agenta:

  1. public agent App : channel Application
  2. {
  3.    public App()
  4.    {
  5.       var a = Adder.CreateInNewDomain();
  6.       while(true)
  7.       {
  8.          Console.Write("X: ");
  9.          int x = Int32.Parse(Console.ReadLine());
  10.          a::X <– x;
  11.  
  12.          Console.Write("X: ");
  13.          int y = Int32.Parse(Console.ReadLine());
  14.          a::Y <– y;
  15.  
  16.          int za = receive(a::Z);
  17.          Console.WriteLine("Adder Z: " + za);
  18.       }
  19.    }
  20. }

Agent będący punktem wejściowym aplikacji musi implementować kanał Application, co w tym wypadku polega jedynie na deklaracji. W konstruktorze (czyli w ciele wykonywania agenta) tworzymy pozostałych potrzebnych nam agentów, w tym wypadku tylko jednego – do dodawania. Zauważcie, że stworzony został w innej domenie niż obecny agent, przez co będzie mógł działać równolegle. Następnie najzwyczajniej w świecie pobieramy liczbę z konsoli i wysyłamy ją na dany port agenta – port który definiuje zgodnie z kanałem. To samo robimy z drugim argumentem. Znaną funkcją receive odbieramy wynik. Ciekawy jest sposób dostępu do portów, a mianowicie nie przez kropkę, jak przywykliśmy do tego typu konstrukcji, a przez podwójny dwukropek.

Myślę, że kod jest na tyle prosty, że każdy zrozumiał ideę, ale teraz więcej szczegółów.

  • każdy agent to oddzielny wątek
  • wysyłanie na porty odbywa się przez kopiowanie i jest nieblokujące
  • porty mogą być jedynie typów niezmiennych (immutable) lub w pełni serializowanych
  • możliwe są operacje Multiplex, Combine, Broadcast, Alternate
  • kanały są kompatybilne z WCF

Axum to naprawdę potężne narzędzie, które bez problemu można wpleść w istniejące projekty .NETowe. Jest skalowane, intuicyjne i proste w zaadaptowaniu. W powyższym wpisie przedstawiłem jedynie  zarys funkcjonalności, ale wszystkich gorąco zachęcam do przyjrzeniu się temu narzędziu bliżej.

Dostępna jest wersja Axum zarówno do VisualStudio 2008 oraz 2010 [2]. Dla ciekawskich powyższy projekt powiększony o jeszcze jednego agenta prezentuje na swoim googlowym hostingu kodu [3]. Zapraszam!

Źródła:

Promuj