7

I may be doing something incorrect, but it is not apparent. I have the following code:

 namespace test
    {
        class Program
            {
             static void Main(string[] args)

                {
                    using (var system = ActorSystem.Create("MySystem"))
                    {
                        var testPassRetriever = system.ActorOf<PrintActor>();
                        var task = testPassRetriever.Ask<PrintActorMsg>(new PrintActorMsg());

                        // prevent the application from exiting before message is handled
                        task.Wait();
                        Console.WriteLine("Finished.");
                        Console.ReadLine();
                    }
                }
        }
        class PrintActorMsg{}

        class PrintActor : ReceiveActor
        {
            public PrintActor()
            {
             Receive<PrintActorMsg>(msg => Console.WriteLine("foo"));
            }
        }
}// namespace test

The issue is that the Task returned by Ask never completes. Its status stays in the Waiting for Activation state. "Foo" does get printed on the command line so I know the actor is processing the Print message. Is there something else I am supposed to do in the overridden actor PrintMsg to mark the task completed?

Matt Johnson
  • 1,913
  • 16
  • 23
  • Wouldn't Task.Run benefit you here? – whoisj Jun 01 '15 at 21:08
  • 2
    I don't think so, I believe that the task is already running (handled by the Akka.net framework). I am getting results of the task being successfully run. I just wait for ever on task completion. – Matt Johnson Jun 01 '15 at 21:11
  • Try something like `Task.Run(async ()=> { /* your using statement */).Wait();` and see if that helps at all. And drop that `while (...)` and `task.Wait;` stuff. – whoisj Jun 01 '15 at 21:12
  • 1
    I think that doing that would bypass AKKA.net and would not be helpful, since I am trying to use Akka.net to handle the multithreading for me. – Matt Johnson Jun 01 '15 at 21:15
  • The library will still manage threads just fine. Give it a try and see if it unblocks you. – whoisj Jun 01 '15 at 21:16
  • 1
    It does not. The wait call never returns. Task.Run(() => testPassRetriever.Ask(new PrintActorMsg())).Wait(); – Matt Johnson Jun 01 '15 at 21:18
  • I'm sorry, I should have specified that you'll need to change `var task = testPassRetriever.Ask(new PrintActorMsg());` to `await testPassRetriever.Ask(new PrintActorMsg());`. Use of the await operator passes control to the scheduler allowing the task to run. – whoisj Jun 01 '15 at 21:19
  • Additionally, you need that `async` keyword. Literally `Task.Run(async ()=> { /* setup code */ await testPassRetriever.Ask(new PrintActorMsg()); /* teardown code */ });` – whoisj Jun 01 '15 at 21:23
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/79364/discussion-between-whoisj-and-matt-johnson). – whoisj Jun 01 '15 at 21:23
  • If I do that then then Console.WriteLine("Finished.") gets called before the message gets processed, which is what I am trying to avoid. – Matt Johnson Jun 01 '15 at 21:23
  • You definitely shouldn't be using `await` / `async` with Akka.NET, because it can break the the "actors process one message at a time" guarantee. Blocking with a `Wait` call is preferred here. ( Source: https://petabridge.com/blog/akkadotnet-async-actors-using-pipeto/ ) -- The real problem is that you need to send a response (see @Martijn's answer below). – BrainSlugs83 Aug 19 '15 at 19:12

2 Answers2

11

You use the ask pattern, but never send a message back. The ask task will only be completed when a message is received from the actor. The (sometimes advised) tell or fire-and-forget pattern doesn't do this.

Martijn
  • 11,964
  • 12
  • 50
  • 96
0

Just for completeness for future readers, since OP's original question didn't appear to want any response / result payload back from the called Actor, the OP should be using the Tell pattern, not Ask, e.g. for fire and forget dispatch scenarios:

 class PrintActor : ReceiveActor
 {
    public PrintActor()
    {
        // Response to the message, with no response
        Receive<PrintActorMsg>(msg => Console.WriteLine("foo"));
    }
 }

And in the calling program

var myActor = system.ActorOf<PrintActor>();
// One way 'fire and forget'
await myActor.Tell(new PrintActorMsg());

Whereas if Ask request-response type interaction is required, then the receive actor needs to provide a response via an explicit Tell back to the sender (PrintResponse is a new response message class):

 public class ResponseActor : ReceiveActor
 {
    public ResponseActor()
    {
       Receive<PrintActorMsg>(msg => {
         Console.WriteLine("foo"));

         // ... Other handling code here

         // Must return a response for an Ask
         Sender.Tell(new PrintResponse(), Self);
       });
    }
 }

And called like so

var response = await actor.Ask<PrintResponse>(new PrintActorMsg(), TimeSpan.FromSeconds(5));

Note that adding exception handling is also a good idea.

StuartLC
  • 104,537
  • 17
  • 209
  • 285