2

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

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

自动机:

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)
  }
}
4

1 回答 1

2

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

在我看来,有两种可能的答案:

  1. 这没有意义。按照设计,您的每个状态参与者都没有这样完成,因为它仍然可能随时需要更多输入词并且只是在等待它们。完成的是发送要检查的单词的代码。

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

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

简而言之:您应该在类型中查找!!演员的运算符Future

编辑:很抱歉造成混乱。正如 drexin 在评论中指出的那样,Akka 2 确实改变了这种情况。请参阅Futures 的 Akka 文档,了解如何发送消息以检索未来。

至于仅检索None:当然,演员必须返回某种有意义的值(如Boolean上面提出的)。再次查看文档以了解如何从参与者发送回复(基本上,sender ! true)。请注意,当然,只有基本情况可以返回trueor false。当你调用另一个演员时,你将再次获得一个未来,为了避免死锁,你必须直接返回它。

因此,在调用方您将不得不连续等待这些期货,即等待最外层的未来评估另一个未来。然后等待这个未来,依此类推,直到您检索到一个非未来的布尔结果值。

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

于 2012-05-29T13:49:55.300 回答