In Getting Started with Akka.NET we created a calculator in Akka.NET that was able to add two numbers and return the response.
In this post we’ll add more functionality to the calculator.
Code for this post: https://github.com/HCanber/akka.net-blog-examples/tree/master/02-calculator
All posts in this series: Tutorials for Akka.NET
Note! This post was written using Akka.NET 0.7.0 and might not work for later versions
Receiving more messages
The calculator should be able to subtract two values as well, so create a copy of the Add
class and rename it to Subtract
public class Subtract
{
private readonly double _term1;
private readonly double _term2;
public Subtract(double term1, double term2)
{
_term1 = term1;
_term2 = term2;
}
public double Term1 { get { return _term1; } }
public double Term2 { get { return _term2; } }
}
We make the fields readonly to ensure the message is immutable.
Next, we need to tell CalculatorActor
how to handle Subtract
messages by adding another Receive
statement:
public class CalculatorActor : ReceiveActor
{
public CalculatorActor()
{
Receive<Add>(add => Sender.Tell(new Answer(add.Term1 + add.Term2)));
Receive<Subtract>(sub=> Sender.Tell(new Answer(sub.Term1 - sub.Term2)));
}
}
You can have as many Receive
statements you like, but be aware of that the order is important. The upper takes precedence over the lower. In this case it doesn’t matter, but if we were to insert a Receive<object>
first, it would handle ALL messages, and our handlers for Add and Subtract would never be called.
Testing subtraction
We add two more lines to test subtraction:
static void Main(string[] args)
{
var system = ActorSystem.Create("calculator-system");
var calculator = system.ActorOf<CalculatorActor>("calculator");
var answer = calculator.Ask<Answer>(new Add(1, 2)).Result;
Console.WriteLine("1 + 2 = " + answer.Value);
var answerSubtract = calculator.Ask<Answer>(new Subtract(5, 3)).Result;
Console.WriteLine("5 - 3 = " + answerSubtract.Value);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
Adding state
Let’s add some state to our CalculatorActor
. Every time it has performed a calculation it should store the answer. We modify the actor to look like this.
public class CalculatorActor : ReceiveActor
{
public CalculatorActor()
{
var answer = 0d;
Receive<Add>(add =>
{
answer = add.Term1 + add.Term2;
Sender.Tell(new Answer(answer));
});
Receive<Subtract>(sub =>
{
answer = sub.Term1 - sub.Term2;
Sender.Tell(new Answer(answer));
});
}
}
Notice that we do not need to declare any fields, we can use locally declared variables.
Concurrency
Suppose we send Add(1,1)
and Subtract(555,22)
to the actor. The actor start processing Add(1, 1)
assign answer=2
and then at the same time Subtract(555,111)
calculates and assigns answer=444
. When the handler of Add(1,1)
creates the reply it will be Answer(444)
which is clearly wrong.
How can we make sure this will not happen?
You don’t need to do anything. Akka.Net handles this for you. An actor can only process one message at the time. So if it has started processing Add(1,1)
it will not start processing anything else until it has finished processing Add(1,1)
.
Retrieve state
So how can we get the latest answer from CalculatorActor
now that answer
is stored in a local variable inside the constructor? Can we make it a public field and then use that to access the value?
No, that will not help! Remember this line when we created the actor:
ActorRef calculator = system.ActorOf<CalculatorActor>("calculator");
ActorOf
returns an ActorRef
, not a CalculatorActor
so we do not have access to public members on CalculatorActor
. And that’s a good thing, as doing so would open up for sharing state.
Returning to one of the fundamentals of Actor based systems:
The only way to communicate with an actor is by messages.
So we need to send it a message GetLastAnswer
and let it reply with an Answer
message.
First the new message. Since it doesn’t store any values, we create it as a singleton:
public class GetLastAnswer
{
private static readonly GetLastAnswer _instance=new GetLastAnswer();
private GetLastAnswer() {}
public static GetLastAnswer Instance { get { return _instance; } }
}
Next we declare what CalculatorActor
should do when it receives a GetLastAnswer
Receive<GetLastAnswer>(m => Sender.Tell(new Answer(answer)));
And then to test it, from Main()
we ask for the last answer after the subtraction:
var answerSubtract = calculator.Ask<Answer>(new Subtract(5, 3)).Result;
Console.WriteLine("5 - 3 = " + answerSubtract.Value);
var lastAnswer = calculator.Ask<Answer>(GetLastAnswer.Instance).Result;
Console.WriteLine("Last answer = " + lastAnswer.Value);
If you run this you should see:
1 + 2 = 3
5 - 3 = 2
Last answer = 2
Code
One file containing all code:
https://github.com/HCanber/akka.net-blog-examples/blob/master/02-calculator/CalculatorApp/Program.cs
Entire project: https://github.com/HCanber/akka.net-blog-examples/tree/master/02-calculator
All posts in this series: Tutorials for Akka.NET
No comments:
Post a Comment