我想只使用Akka actor作为邮箱,即我想创建n个线程,每个线程创建一个远程actor。
每个线程获得对其他线程的所有远程actor的引用,以便它们可以通过各自的actor向彼此发送消息。
演员的定义如下:
case class Receive
case class GroupReceive(id: Symbol)
case class GroupMsg[T](id: Symbol, msg: T)
class FooParActor(val distributor: Distributor) extends Actor
with Stash {
import context._
val globalRank: Int = distributor.globalRank
def doReceive(realSender: ActorRef, ID: Symbol) {
unstashAll()
become({
case GroupMsg(ID, msg) =>
realSender ! msg
unbecome()
case GroupMsg(otherId, msg) =>
println(globalRank + ": stashing " + otherId)
unbecome()
case x => sys.error("bad msg: " + x)
}, discardOld = false)
}
def receive = {
case GroupReceive(id) =>
doReceive(sender, id)
case GroupMsg(id, x) =>
stash()
case x => sys.error("bad msg: " + x)
}
}
要读取消息,所有者线程将GroupReceive('someSymbol)
发送给他的本地actor,后者又将GroupMsg转发给该线程。从线程的角度来看,读取消息的代码如下所示:
def groupRcv[T](id:Symbol) = Await.result(aref ? GroupReceive(id), timeout.duration).asInstanceOf[T]
其中aref
是对此线程的本地actor的引用。
我有时会遇到上述模式的死锁(超时5秒),即使使用极其简单和消息也很少。在收到GroupReceive(id)
消息之后,但在输入第一个doReceive(...)案例之前,我将问题缩小为演员停滞:case GroupMsg(ID, msg) =>
。
我制作了printout-traces来检查演员在进入doReceive调用之前是否真的在存储中有消息,而且似乎由于某种原因,他们只是不处理它们。我上面提到的代码可以进入GroupMsg()
从FooParActor
藏匿中丢失的状态吗?或者,在收到GroupReceive()
消息后,演员可以采取其他任何方式陷入僵局吗?
答案 0 :(得分:2)
您正在使用Await.result()
但不能分享您执行此操作的位置:如果您在演员应该运行的线程上调用groupRcv
,那么您当然可以饿死(即目标) actor没有可用于运行的线程,因此它永远不会完成请求。)
您似乎正在以不健康的方式将基于线程的并发与actor混合在一起,但由于您只提示它并且不显示代码,因此我只能给您提供不做的广泛建议。编程演员时,忘记线程;那些是由Akka管理的。特别是不要滥用Akka的线程(即Await.result
可能会在你自己的外部线程池上工作,尽管几乎总有一个更好的选择)。
最后,如果你使用actor只是制作“带邮箱的线程”,那么Akka无法帮助你,你会遇到所有常见的传统并发陷阱。