-
Notifications
You must be signed in to change notification settings - Fork 1
Modes of Operation
Synchronous operations are always faster than asynchronous operations, but there are times when asynchronous operation is required. Worse, an asynchronous operation typically requires many other operations to be asynchronous as well. This is why it is a very good thing to write code which can operate both synchronously and asynchronously.
The Actor class, and its subclasses, process messages both synchronously and asynchronously. The timing of these two modes of operation differs most when message processing is brief, with synchronous processing being about 110 times (11,000%) faster than asynchronous processing.
For an actor to operate asynchronously, it requires a mailbox. Incoming messages which can not be processed synchronously are added to the actor's mailbox for processing on a separate thread. The deciding factor for when a message must be processed asynchronously is determined by the mailbox of the actor which sent the request and the mailbox of the actor which received the request. When both actors are using the same mailbox, the receiving actor can safely process the message synchronously. Otherwise the message must be processed asynchronously.
There is one exception to this rule. Immutable actors, which are actors whose state never changes, can safely process all the messages they receive synchronously and do not use a mailbox. We designate an actor as immutable by constructing it without a mailbox. But there are restrictions placed on immutable actors.
- An immutable actor must not change its state.
- The state of an immutable actor must not be changed by any other means. And
- An immutable actor can only send messages to other immutable actors.
The behavior of an actor does differ depending on its mode of operation, so some care needs to be exercised in the patterns we use to ensure that the code is not sensitive to these differences. The difference between synchronous and asynchronous operation is, of course, the time when a message is processed. Time to look at some code.
case class AMessage()
class A(mb: Mailbox, sub: Actor) extends Actor(mb, null) {
bind(classOf[AMessage], afunc)
afunc(msg: AnyRef, rf: Any => Unit) {
println("start afunc")
sub(msg) {rsp =>
println("got result")
rf("all done")
}
println("end afunc")
}
}
class B(mb: Mailbox) extends Actor(mb, null) {
bind(classOf[AMessage], bfunc)
bfunc(msg: AnyRef, rf: Any => Unit){rf("ta ta")}
}
val mb1 = new Mailbox
val b = new B(mb1)
val mb2 = new Mailbox
println("synchronous test")
Future(new A(mb1, b), AMessage())
println("asynchronous test")
Future(new A(mb2, b), AMessage())