使用akka路由器,我要执行两个不同的操作。但是当我传递消息时,它们是重叠的。这是我的代码。
class Master extends Actor {
import context._
val numRoutees = 3
val router: ActorRef = actorOf (RoundRobinPool (numRoutees).props(Props[Worker]), "router")
// broadcasts GetString() and receives a random string from each routee
def stringMessages(replies: Set[String] = Set()): Receive = {
case GetString() =>
router ! Broadcast(GetString()) // g
case reply: String =>
val updatedReplies = replies + reply
if (updatedReplies.size == numRoutees) {
println("result = " + updatedReplies.mkString("[", ",", "]"))
}
become(stringMessages(updatedReplies))
case GetInteger() =>
become(intMessages())
// self ! createArray() // h // <- uncommenting this results in an infinte loop
case _ => println("stringMessages: no matches")
}
// broadcasts GetInteger and receives a random integer from each routee
def intMessages(ints: Set[Int] = Set()): Receive = {
case GetInteger() =>
router ! Broadcast(GetInteger()) // e
case n: Int =>
val updatedInts = ints + n
if (updatedInts.size == numRoutees) {
println("result = " + updatedInts.mkString("[", ",", "]"))
}
become(intMessages(updatedInts))
case GetString() =>
become(stringMessages())
self ! GetString() // f
case _ => println("intMessages: no matches")
}
override def receive: Receive =
{
case GetString() =>
become(stringMessages())
self ! GetString() // c
case GetInteger() =>
become(intMessages())
self ! GetInteger() // d
case _ => println("root doesn't match")
}
}
object MasterTest extends App {
val system = ActorSystem ("ActorSystem")
val actor = system.actorOf(Props[Master], "root")
actor ! GetInteger() // a
actor ! GetString() // b
}
我了解到一些调试语句的执行顺序可能是a -> b -> f -> g
。 (请注意代码中注释的语句的ID)。该代码不执行我期望的操作。输出是
result = [a,b,c]
如何使它们按a -> d-> e -> b -> f -> g
的顺序执行。如果我添加一个Thread.sleep
之类的
actor ! GetInteger() // a
Thread.sleep(3000)
actor ! GetString() // b
我得到了预期的输出,即
result = [0,4,6] // random integers
result = [a,b,c] // random strings
如何使actor消息排队,以使新消息仅在完全执行前一个消息之后才执行。有什么更好的方式来实现我在become()
中所做的工作?如果我想在代码中添加更多状态(例如此处的GetInteger
和GetString
),则代码变得太多余了,无法使用become(newState)
将状态更改为另一状态。
如果我取消注释h
,代码也会变成a -> b -> f -> d -> h -> f -> h -> f -> ...
的无限循环。因此,我首先了解到这不是正确的实现。
答案 0 :(得分:1)
一个想法是将String
答复和Int
答复都编码为单个Receive
行为。例如:
case object GetInteger
case object GetString
// ...
def handleMessages(intReplies: Set[Int] = Set(), strReplies: Set[String] = Set()): Receive = {
case GetInteger =>
router ! Broadcast(GetInteger)
case GetString =>
router ! Broadcast(GetString)
case i: Int =>
val updatedInts = intReplies + i
if (updatedInts.size == numRoutees) {
println("result = " + updatedInts.mkString("[", ",", "]"))
}
become(handleMessages(updatedInts, strReplies))
case str: String =>
val updatedStrings = strReplies + str
if (updatedStrings.size == numRoutees) {
println("result = " + updatedStrings.mkString("[", ",", "]"))
}
become(handleMessages(intReplies, updatedStrings))
case x =>
println("Not an Int or String: " + x)
}
def receive = handleMessages
请注意,我将GetInteger
和GetString
更改为case对象而不是case类,因为它们没有参数。这样做可以让您在最后加上括号(即,您可以使用GetInteger
代替GetInteger()
)。
此外,如果您担心订单,请考虑使用有序集合,例如scala.collection.immutable.Seq
而不是Set
(无序)。