检查actor系统是否已完成其工作

时间:2012-05-29 13:18:40

标签: scala akka

我写了一个基于Akka的自动机,可以接受1到20的罗马数字。自动机工作正常,但在处理完整输入后我想关闭系统。

因为我不知道自动机什么时候结束,我不知道何时发送停止信号。这导致问题,即系统过早停止并且自动机无法完成其任务。检查actor系统中是否有任务尚未完成的最佳方法是什么?

自动机:

import akka.actor.{Props, ActorSystem, ActorRef, Actor}

object RomanNumberAcceptor extends App {

  val allLegalNumbers = List("I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
    "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX"
  )

  val someWrongNumbers = List("IXX", "VV", "VX", "IIII", "IVV", "XXX", "XXI", "XXV", "IIX", "IIIV")

  for (number <- allLegalNumbers)
    State testNumber number

  for (number <- someWrongNumbers)
    State testNumber number

  // this stop signal is too early
  State.stop()
}

case class WordData(fullWord: String, remainder: String)

object State {
  val ErrorStateId: Int = -1

  private val data = Map(
    0 -> (Map('I' -> 1, 'V' -> 5, 'X' -> 10), false),
    1 -> (Map('I' -> 2, 'V' -> 4, 'X' -> 9), true),
    2 -> (Map('I' -> 3, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    3 -> (Map('I' -> ErrorStateId, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    4 -> (Map('I' -> ErrorStateId, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    5 -> (Map('I' -> 6, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    6 -> (Map('I' -> 2, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    9 -> (Map('I' -> ErrorStateId, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    10 -> (Map('I' -> 1, 'V' -> 5, 'X' -> 20), true),
    20 -> (Map('I' -> ErrorStateId, 'V' -> ErrorStateId, 'X' -> ErrorStateId), true),
    ErrorStateId -> (Map.empty[Char, Int], false)
  )

  val system = ActorSystem("RomanNumberAcceptor")

  val states: Map[Int, ActorRef] =
    for ((id, (map, accepted)) <- State.data)
      yield id -> system.actorOf(Props(State(id, map, accepted)), name = "s"+id)

  def testNumber(s: String) {
    states(0) ! WordData(s, s)
  }

  def stop() {
    for (a <- states.values)
      system stop a
    system.shutdown()
  }
}

case class State(id: Int, transMap: Map[Char, Int], accepted: Boolean) extends Actor {
  def receive = {
    case WordData(fullWord, _) if `id` == State.ErrorStateId =>
      println("Error: "+fullWord)
    case WordData(fullWord, remainder) if remainder.isEmpty =>
      println((if (accepted) "Success: " else "Error: ") + fullWord)
    case WordData(fullword, remainder) =>
      // maybe some heavy operation here
      (0 to 1e4.toInt).sum

      val nextAktor = transMap(remainder.head)
      State.states(nextAktor) ! WordData(fullword, remainder.tail)
  }
}

1 个答案:

答案 0 :(得分:2)

(以下假设Scala的非Akka演员。请参阅下面的编辑,了解如何调整它以与Akka一起使用)

我认为有两种可能的答案:

  1. 没有意义。按照设计,你的每个州演员都不会完成,因为它可能在任何时候仍然需要更多输入词而只是等待它们。完成的是发送要检查的单词的代码。

  2. 虽然您似乎没有为WordData消息返回任何有用的值,但仍可以等待返回值。您应该特别关注!!!?运算符的用法。通过将它们链接到不同的演员身上,你可以等待他们处理这个词。使用!?时要小心,因为重入状态可能会导致死锁。

  3. 相反,我建议您为Boolean消息定义一个WordData答案,告诉您该字词是否被接受。然后,您可以使用!!来检索未来(取决于另一个未来,这取决于另一个未来,依此类推单词的长度),然后testNumber方法可以阻止检索接受值。如果您想要并行测试多个单词,您还可以让testNumber返回Future本身,并在调用App之前阻止stop对象中的所有这些未来。

    简而言之:您应该使用!!类型为参与者查找Future运算符。

    编辑:对不起,请注意。正如drexin在评论中指出的那样,这确实随着Akka 2而改变。请参阅Akka documentation of Futures了解如何发送消息以便检索未来。

    至于仅检索None:当然,参与者必须返回某种有意义的值(如上面提出的Boolean)。请再次查看documentation如何发送演员的回复(基本上是sender ! true)。请注意,当然,只有基本案例可以返回truefalse。当你打电话给另一个演员时,你将再次获得未来并避免死锁,你将不得不直接返回。

    因此,在呼叫方面,你将不得不先后等待这些未来,即等待最外面的未来来评估另一个未来。然后等待这个未来,依此类推,直到你检索到一个非未来的布尔结果值。

    从你的actor中调用下一个actor并返回相应未来的评估(而不是未来本身)的问题意味着将阻止调用actor。因此,当使用阻塞!?调用时,这将导致与传统Scala actor相同的死锁行为。