I know about PipeTo, but some stuff, like synchronous waiting on nested continuation, seems to go against the async & await way.
So, my first question [1] would be:
Just to add to what Aaron said. As of yesterday, we do support safe async await inside actors when using the Task dispatcher.
public class AsyncAwaitActor : ReceiveActor
{
public AsyncAwaitActor()
{
Receive(async m =>
{
await Task.Delay(TimeSpan.FromSeconds(1));
Sender.Tell("done");
});
}
}
public class AskerActor : ReceiveActor
{
public AskerActor(ActorRef other)
{
Receive(async m =>
{
var res = await other.Ask(m);
Sender.Tell(res);
});
}
}
public class ActorAsyncAwaitSpec : AkkaSpec
{
[Fact]
public async Task Actors_should_be_able_to_async_await_ask_message_loop()
{
var actor = Sys.ActorOf(Props.Create()
.WithDispatcher("akka.actor.task-dispatcher"),
"Worker");
//IMPORTANT: you must use the akka.actor.task-dispatcher
//otherwise async await is not safe
var asker = Sys.ActorOf(Props.Create(() => new AskerActor(actor))
.WithDispatcher("akka.actor.task-dispatcher"),
"Asker");
var res = await asker.Ask("something");
Assert.Equal("done", res);
}
}
This is not our default dispatcher since it does come with a price in performance/throughput.
There is also a risk of deadlocks if you trigger tasks that block(e.g. using task.Wait()
or task.Result
)
So the PipeTo
pattern is still the preferred approach since it is more true to the actor model.
But the async await support is there for you as an extra tool if you really need to do some TPL integration.
This feature actually uses PipeTo
under the covers.
It will take every task continuation and wrap that up in a special message and pass that message back to the actor and execute that task inside the actors own concurrency context.