仅使用Akka actor作为邮箱的死锁

时间:2013-03-05 19:03:51

标签: scala message-queue deadlock akka actor

我想只使用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()消息后,演员可以采取其他任何方式陷入僵局吗?

1 个答案:

答案 0 :(得分:2)

您正在使用Await.result()但不能分享您执行此操作的位置:如果您在演员应该运行的线程上调用groupRcv,那么您当然可以饿死(即目标) actor没有可用于运行的线程,因此它永远不会完成请求。)

您似乎正在以不健康的方式将基于线程的并发与actor混合在一起,但由于您只提示它并且不显示代码,因此我只能给您提供不做的广泛建议。编程演员时,忘记线程;那些是由Akka管理的。特别是不要滥用Akka的线程(即Await.result可能会在你自己的外部线程池上工作,尽管几乎总有一个更好的选择)。

最后,如果你使用actor只是制作“带邮箱的线程”,那么Akka无法帮助你,你会遇到所有常见的传统并发陷阱。