12

I want to interact with Akka actors from my own thread. Currently, i do like so:

val res = Await.result(aref ? GroupReceive(fromRank), timeout.duration).asInstanceOf[T]

But I am unsure how this actually interacts with my thread? I wish for the receive to be asynchronous, i.e. I want to hang up the thread while receiving to allow for some other work to be done. I just recently read about Akka inbox system. inbox akka api

I think I recall that Await creates a new actor every time. What are the differences between await+ask and inbox, and can someone give me an example of how to create an inbox and use it to communicate with actors from "the outside" ?

EDIT Just to clarify, I don't want the same thread to continue working, I want it to stop hogging a cpu-core and leave other threads to work until it receives something, then wake up again.

Felix
  • 8,385
  • 10
  • 40
  • 59
  • 2
    In akka you should better use `mapTo[Type]` instead of `asInstanceOf` – 4lex1v May 24 '13 at 09:04
  • What is the benefit? :) – Felix May 24 '13 at 09:05
  • 1
    `asInstanceOf` throws an exception, when it fails. `mapTo` returns a failed future. – drexin May 24 '13 at 09:06
  • 1
    The dangerous thing is, that `asInstanceOf` might actually succeed and fail later, when a specific method is called. – drexin May 24 '13 at 09:07
  • Also Await can't create new actors, it's sole purpose is to block execution until awaiting for the result. The only way to create an actor is through `system.actorOf(...)` – 4lex1v May 24 '13 at 09:11
  • Does it hog the thread though? – Felix May 24 '13 at 09:13
  • 1
    Yes it is, you can read about use of akka and futures [here](http://doc.akka.io/docs/akka/2.2-M3/scala/futures.html) – 4lex1v May 24 '13 at 09:14
  • 1
    There is an example in the [documentation](http://doc.akka.io/api/akka/2.1-M2/index.html#akka.actor.ActorDSL$). – senia May 24 '13 at 09:15
  • @AlexIv: `system.actorOf(...)` is not the only way to create an actor. `ask` or `?` [creates](https://github.com/akka/akka/blob/master/akka-actor/src/main/scala/akka/pattern/AskSupport.scala#L160) a new actor, `ActorDSL.inbox` creates a new actor. – senia May 24 '13 at 09:27
  • @senia sorry never used akka's dsl, but as i can understand from [this line](https://github.com/akka/akka/blob/master/akka-actor/src/main/scala/akka/pattern/AskSupport.scala#L160) it doesn't create a new actor, but returns a future with an actor reference – 4lex1v May 24 '13 at 09:31
  • @senia it returns a new ActorRef with ActorSelection mechanism, but not a new Actor – 4lex1v May 24 '13 at 09:44

3 Answers3

9

As written in the Akka's Future documentation, using Await blocks current thread until awaiting for the result.

Example

import scala.concurrent.Await
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.duration._

implicit val timeout = Timeout(5 seconds)
val future = actor ? msg // enabled by the “ask” import
val result = Await.result(future, timeout.duration).asInstanceOf[String]

This will cause the current thread to block and wait for the Actor to 'complete' the Future with it's reply.

Use With Actors

4lex1v
  • 21,367
  • 6
  • 52
  • 86
  • Can you elaborate on the difference between this and the use of inbox? – Felix May 24 '13 at 09:23
  • @Felix Sorry never used akka's dsl, but as i can see the main usage of inbox is that ask can't 1) receiving multiple replies and 2) watching other actors’ lifecycle. [The Inbox](http://doc.akka.io/docs/akka/2.2-M3/scala/actors.html) – 4lex1v May 24 '13 at 09:33
  • @senia still disagree with you, ask return a Future with ActorRef through an ActorSelection, but it doesn't creates a new Actor – 4lex1v May 24 '13 at 09:52
  • @Alexlv, if you use ask outside of an Actor, a temporary actor is created under the hood. This is because in order to respond you need an actor ref to send back to via the ! operator. The actor is short lived and is stopped after the response is received or a timeout occurs. – cmbaxter May 24 '13 at 10:50
  • how could I use this with a BroadcastRouter that should reply several times for each future? Thank you. – Trylks Aug 20 '13 at 15:48
  • No need to be blocking. As mentioned in the docs you can import scala.concurrent.Future and akka.pattern.ask, then ```val future: Future[String] = ask(actor, msg).mapTo[String]``` – matanster Dec 06 '14 at 12:00
5

The Await.receive is part of the Scala concurrency API and has nothing to do with actors. Its purpose is the block the current thread until the provided future completes, or the timeout limit kicks in and the whole thing ends in a timeout exception.

The ask operator ? will indeed create a temporary actor with the sole purpose of awaiting for the reply from the actor pointed to by the aref variable and completing the future you got when you called the ask operator with the received reply.

So your code is essentially blocking the entire thread. As it was indicated, if you want to free up the current thread and continue doing some other work you can attach a callback to the future.

implicit val ctx: ExecutionContext = //provide execution context here
implicit val timeout: Timeout = // provide timeout here
aref ? GroupReceive(fromRank)) onSuccess { res =>
   //do something with res here, asynchronously
}
// some other code which runs without being blocked...

The above code can be rewritten with the actor DSL you mentioned above:

import akka.actor.ActorDSL._
implicit val actorSystem: ActorSystem = // provide an actor system here or any actor ref factory

actor(new Act {
  aref ! GroupReceive(fromRank)
  context.setReceiveTimeout(timeout) //optional
  become {
    case ReceiveTimeout => {
      //handle the timeout
      context.stop(self)
    }
    case res => {
      //do your thing with res, asynchronously
      context.stop(self)
    }
  }
}

//some other code which won't wait for the above operations

The latter version also creates a new temporary actor which sends the GroupReceive message and then waits for a reply, after which it kills itself.

The bottom line is that in order to receive a message from an actor you must be an actor yourself. Actors can't just send message to something other than an ActorRef.

So either you use the ask pattern which creates a temporary actor behind the scenes and manages this temporary actor's lifecycle itself, exposing to you just a nice simple future to work with, or you can create the temporary actor yourself, but then you have to manage its lifecycle (i.e. remember to kill it once it has done its job)

Choose the option that suits you best.

Marius Danila
  • 10,311
  • 2
  • 34
  • 37
  • Regarding the DSL example part of your response: although this works for higher latency requests, for very quick request/response it may fail because become takes some time to kick in. A DSL action should be initialised exactly like in this question to always work: http://stackoverflow.com/questions/17851849/the-actor-dsl-example-from-akka-doc. –  Apr 20 '15 at 00:00
0

If you don't want to block on the calling side then don't use Await, use the non blocking callbacks instead like onSuccess, onFailure and onComplete. When you do this, a future task is put into whatever ExecutionContext is in scope at time of ask (?). When a response is received, this callback is invoked asynchronously via the ExecutionContext. This way you avoid blocking all together in the thread that is making the request to the actor and then the callback is handled in the thread pool tied to the ExecutionContext.

In addition, I believe the inbox stuff you mention is geared towards testing out actor stuff in the REPL (at least that's what the docs on ActorDsl) states. Stick with the approach you have of using ask from outside the actor. Let akka create the short lived actor that it needs for the communication under the hood for non-actor to actor calls. Then just switch to a non blocking callback as I suggested above. I believe that's what you are looking for.

cmbaxter
  • 35,283
  • 4
  • 86
  • 95