akka.net

Handling a received not covered in become


I am using Akka.NET to develop a logistics simulation.

Having tried various patterns, it seems to me that FSM-type behaviour using become will substantially simplify development.

The system has a repeating clock tick message that all relevant actors receive in order to simulate accelerated passage of time for the entire simulation system. This clock tick message should be handled by all actors that are subscribed to it regardless of which message loop is currently active for any specific actor.

Am I correct in thinking that the only way to handle the clock message in all message loops is by explicitly checking for it in all message loops, or is there a way of defining messages that are handled regardless of which message loop is active?

If the former is the case my idea is to check for a clock tick message in a ReceiveAny, which all the message loops need to have anyway, and to then pass it on to an appropriate handler.


Solution

  • You could use Stashing to Stash the messages while Simulating. I came up with the following code sample to better explain how that works:

    // See https://aka.ms/new-console-template for more information
    using Akka.Actor;
    using Akka.NET_StackOverflow_Questions_tryout.Questions;
    
    
    var actorSystem = ActorSystem.Create("stackOverFlow");
    var sim = actorSystem.ActorOf(Props.Create(()=> new StackOverflow71079733()));
    sim.Tell(5000L);
    sim.Tell("string");
    sim.Tell(1000L);
    sim.Tell("strin2");
    sim.Tell("strin3");
    Console.ReadLine();
    
    
    public class StackOverflow71079733 : ReceiveActor, IWithUnboundedStash
        {
            public IStash Stash { get ; set ; }
            private readonly IActorRef _simActor;
            public StackOverflow71079733()
            {
                _simActor = Context.ActorOf<SimulationActor>();
                ClockTickMessage();
            }
            private void Simulate(long ticks)
            {
                Console.WriteLine($"Ticks: {ticks}");
    
                Receive<Done>(d => 
                {
                    Console.WriteLine("Simulation done");
                    Become(ClockTickMessage);   
                    Stash?.Unstash();    
                });
    
                // you can add additional messages that may to be handled while the simulation is happening
                // e.g: 
                Receive<string>(s => Console.WriteLine($"received in '{s}' in simulation"));
    
                //While the simulation is on-going, add the incoming message into a queue/stash it
                // so that it is not lost and can be picked and handled after stimulation is done
                ReceiveAny(any =>
                {
                    Stash.Stash();
                    Console.WriteLine($"Stashed Ticks: {any}");
                });
                _simActor.Tell(ticks);
            }
            private void ClockTickMessage()
            {
                // you can create an object to represent the ClockTickMessage
                Receive<long>(ticks => 
                {
                    Become(() => Simulate(ticks));
                });
            }
        }
        /// <summary>
        /// We need to run simulation in a another actor so that the parent actor can keep receiving ClockTicksMessages
        /// In case the sim takes a long time to become
        /// </summary>
        public sealed class SimulationActor : ReceiveActor
        {
            private IActorRef _sender;
            public SimulationActor()
            {
                Receive<long>(l =>
                {
                    _sender = Sender;
                    Thread.Sleep(TimeSpan.FromMilliseconds(l));
                    _sender.Tell(Done.Instance);    
                });
            }
        }
        public sealed class Done
        {
            public static Done Instance = new Done();
        }