Akka.net – pierwszy przykład

W ostatnich dwóch wpisach pokazałem zasady działania modelu aktor. W kolejnych postach będę korzystał już z Akka.net zamiast pseudokodu. Dzisiaj czysty opis podstaw API – bez konkretnego problemu do rozwiązania.

Akka.net można zainstalować w formie pakietu Nuget:

Install-Package Akka

Następnie definiujemy wiadomość za pomocą zwyklej klasy typu immutable:

    public class TransferMoney
    {
        public string From { get; private set; }
        public string To { get; private set; }

        public TransferMoney(string from,string to)
        {
            From = @from;
            To = to;
        }
    }

Każdy aktor powinien dziedziczyć np. po ReceiveActor:

    public class TransferMoneyActor : ReceiveActor
    {
        public TransferMoneyActor()
        {
            Receive<TransferMoney>(msg =>
            Console.WriteLine("Transferring money from {0} to {1}", msg.From, msg.To));
        }
    }

Za pomocą Receive definiujemy, jakie wiadomości chcemy obsługiwać. Powyższa klasa zatem będzie odbierać wiadomości typu TransferMoney.
W akka.net aktorzy egzystują w tzw. ActorSystem. Aktorzy z dwóch różnych systemów są od siebie odizolowani. Inicjacja nowego systemu jest prosta:

var system = ActorSystem.Create("JakasNazwa");

Jak wiemy, aktorzy należą do konkretnych systemów, zatem w celu stworzenia aktora należy:

var actor1 = system.ActorOf<TransferMoneyActor>();

Wysłanie wiadomości odbywa się za pomocą metody Tell. Całość wygląda więc następująco:

var system = ActorSystem.Create("JakasNazwa");

var actor1 = system.ActorOf<TransferMoneyActor>();

actor1.Tell(new TransferMoney("nadawca", "odbiorca"));

Z poprzednich wpisów pamiętamy, że wiadomości są kolejkowane i wykonywane asynchronicznie. Stwórzmy zatem następującego aktora:

   public class TransferMoneyActor : ReceiveActor
    {
        public TransferMoneyActor()
        {
            Receive<TransferMoney>(DoWork,shouldHandle:null);
        }

        private void DoWork(TransferMoney msg)
        {
            Console.WriteLine("{0}:{1}",DateTime.Now,Thread.CurrentThread.ManagedThreadId);

            Thread.Sleep(5000);
        }
    }

Jeśli faktycznie wiadomości są kolejkowane i wykonywane jedno po drugim, wtedy powinniśmy zawsze widzieć ten sam identyfikator wątku w odstępach dokładnie 5 sekund. W celu udowodnienia, że zadania są wykonywane asynchronicznie, w pętli wyślemy 10 wiadomości:

            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("{0}: Wysylanie wiadomosci nr {1}", DateTime.Now,i);
                actor1.Tell(new TransferMoney("nadawca", "odbiorca"));
            }

Jeśli są synchronicznie wykonywane, wtedy po wyświetleniu tekstu “Wysylanie wiadomosci nr…”, powinniśmy zobaczyć numer wątku. Jeśli z kolei wykonywane są asynchronicznie, tak jak tego spodziewamy się, wtedy metoda Tell powinna wyłącznie umieścić wiadomość w kolejce.

Screenshot potwierdzający założenia:

tasks

W następnym wpisie, zajmiemy się znów jakimś problemem wielowątkowym, który najpierw rozwiążemy w “klasyczny sposób” z użyciem blokad, a później za pomocą modelu aktor.

Leave a Reply

Your email address will not be published.