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:
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.