我写了一个基于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)
}
}
答案 0 :(得分:2)
(以下假设Scala的非Akka演员。请参阅下面的编辑,了解如何调整它以与Akka一起使用)
我认为有两种可能的答案:
没有意义。按照设计,你的每个州演员都不会完成,因为它可能在任何时候仍然需要更多输入词而只是等待它们。完成的是发送要检查的单词的代码。
虽然您似乎没有为WordData
消息返回任何有用的值,但仍可以等待返回值。您应该特别关注!!
和!?
运算符的用法。通过将它们链接到不同的演员身上,你可以等待他们处理这个词。使用!?
时要小心,因为重入状态可能会导致死锁。
相反,我建议您为Boolean
消息定义一个WordData
答案,告诉您该字词是否被接受。然后,您可以使用!!
来检索未来(取决于另一个未来,这取决于另一个未来,依此类推单词的长度),然后testNumber
方法可以阻止检索接受值。如果您想要并行测试多个单词,您还可以让testNumber
返回Future
本身,并在调用App
之前阻止stop
对象中的所有这些未来。
简而言之:您应该使用!!
类型为参与者查找Future
运算符。
编辑:对不起,请注意。正如drexin在评论中指出的那样,这确实随着Akka 2而改变。请参阅Akka documentation of Futures了解如何发送消息以便检索未来。
至于仅检索None
:当然,参与者必须返回某种有意义的值(如上面提出的Boolean
)。请再次查看documentation如何发送演员的回复(基本上是sender ! true
)。请注意,当然,只有基本案例可以返回true
或false
。当你打电话给另一个演员时,你将再次获得未来并避免死锁,你将不得不直接返回。
因此,在呼叫方面,你将不得不先后等待这些未来,即等待最外面的未来来评估另一个未来。然后等待这个未来,依此类推,直到你检索到一个非未来的布尔结果值。
从你的actor中调用下一个actor并返回相应未来的评估(而不是未来本身)的问题意味着将阻止调用actor。因此,当使用阻塞!?
调用时,这将导致与传统Scala actor相同的死锁行为。