ActorRef.tell和Inbox.send在Akka中有什么区别?

8

我已经开始学习Akka,并在Typesafe中尝试使用示例。例如,Hello Akka应用程序具有以下代码:

    import akka.actor.{ ActorRef, ActorSystem, Props, Actor, Inbox }
import scala.concurrent.duration._

case object Greet
case class WhoToGreet(who: String)
case class Greeting(message: String)

class Greeter extends Actor {
  var greeting = ""

  def receive = {
    case WhoToGreet(who) => greeting = s"hello, $who"
    case Greet           => sender ! Greeting(greeting) // Send the current greeting back to the sender
  }
}

object HelloAkkaScala extends App {

  // Create the 'helloakka' actor system
  val system = ActorSystem("helloakka")

  // Create the 'greeter' actor
  val greeter = system.actorOf(Props[Greeter], "greeter")

  // Create an "actor-in-a-box"
  val inbox = Inbox.create(system)

  // Tell the 'greeter' to change its 'greeting' message
  greeter.tell(WhoToGreet("akka"), ActorRef.noSender)

  // Ask the 'greeter for the latest 'greeting'
  // Reply should go to the "actor-in-a-box"
  inbox.send(greeter, Greet)

  // Wait 5 seconds for the reply with the 'greeting' message
  val Greeting(message1) = inbox.receive(5.seconds)
  println(s"Greeting: $message1")

  // Change the greeting and ask for it again
  greeter.tell(WhoToGreet("typesafe"), ActorRef.noSender)
  inbox.send(greeter, Greet)
  val Greeting(message2) = inbox.receive(5.seconds)
  println(s"Greeting: $message2")

  val greetPrinter = system.actorOf(Props[GreetPrinter])
  // after zero seconds, send a Greet message every second to the greeter with a sender of the greetPrinter
  system.scheduler.schedule(0.seconds, 1.second, greeter, Greet)(system.dispatcher, greetPrinter)

}

// prints a greeting
class GreetPrinter extends Actor {
  def receive = {
    case Greeting(message) => println(message)
  }
}

现在我卡在理解什么是“,”之间的区别。

greeter.tell(WhoToGreet("akka"), ActorRef.noSender)

并且

inbox.send(greeter, Greet)

根据我的理解,actor在结尾处启动了自己的线程。

val inbox = Inbox.create(system)

有人能解释一下Actorref.tell具体是做什么的,然后Inbox.send行实现了什么?

2个回答

14
tell(也表示为)的目的是向一个Actor发送消息。由于演员之间通过消息传递进行通信,因此tell是支持该消息传递的机制。它对调用者是异步的,因此一旦调用者调用了tell,他们就与目标Actor实例接收和处理该消息解耦。在这个特定的示例中,代码使用tell来导致问候Actor更新其内部状态。由于这种交互不会产生任何类型的响应(接收Actor没有回复请求发送方),因此仅使用tell在这里就足够了。
要从问候Actor获取问候响应,交互略有不同。在此交互中,发送方期望收到一个响应消息。这种请求/响应交互可以通过ask(即)来处理,其中调用者得到一个Future,该Future将在接收方答复时完成,但在此处没有使用ask。在这个例子中,编码器通过使用一个像Actor一样的Inbox来获得请求/响应语义,这消除了需要Futures的需求。收件箱必须像ActorRef一样向接收方显示,以便它可以使用以下行将响应路由回来:
sender ! Greeting(greeting)

sender()方法返回当前正在处理的消息的发送者ActorRef。 为了让这个方法正常工作,Inbox必须能够将自己表示为ActorRef。但是,就像我之前说过的,您也可以在这里使用ask来获取请求/响应交互工作。 我认为这只是一个选择问题。 底线是,在需要快速而简单的互动模式时使用tell,在需要请求/响应语义时可以使用收件箱(或另一个Actor或ask)。


那么这是否意味着,如果我注释掉“inbox.send(greeter,Greet)”调用,我仍然会看到Greeter actor类中调用“receive”方法? - Som Bhattacharyya
@SomBhattacharyya,在这个例子中,receive 部分函数将被调用两次;一次来自 WhoToGreettell,另一次来自 Greet 消息的 inbox.send。如果你注释掉 inbox.send,那么 receive 将只为 WhoToGreet 消息调用一次。 - cmbaxter

2
当您执行以下操作时,您是直接处理一个ActorRef
greeter.tell(WhoToGreet("akka"), ActorRef.noSender)

另一方面,当您使用Inbox时,您处理的是一种不完全是actor但类似于actor的东西。这种情况有助于在您不想创建自己的actor但仍想异步地与其他actor交互时使用。请参见此处Inbox是一种类似于actor的对象,可以从外部进行查询。它包含一个actor,其引用可以像往常一样传递给其他actor,并且它可以观察其他actor的生命周期。 该actor在线程池上运行(使用调度程序进行配置)。您可以通过配置或将其放入代码中来决定该actor使用哪个调度程序进行执行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接