子actor的调度程序没有停止并在停止父actor后抛出NPE

时间:2016-10-19 17:36:29

标签: scala akka actor

流量:

  • 我有3个级别的演员(ABC)。
  • A创建BB创建CA --> B --> C
  • BC有两种状态:workingending。在C完成所有内容后,它会进入ending状态,启动调度程序以向CFinished发送B消息。
  • B收到CFinished消息时,它也会进入ending状态,并启动自己的调度程序向BFinished发送A消息
  • A收到BFinished消息时,也会进入ending状态并调用context stop self,停止自己以及他创建的所有孩子。

问题:

看起来调度程序在actor停止后不会停止,下次触发时会抛出NullPointerException

我怎么解决这个问题?

代码:

import scala.concurrent.duration._
import akka.actor.{Actor, ActorLogging, Cancellable, Props}
import com.zinio.damntool.msg.{BFinished, CFinished}


object msg {
  case class BFinished()
  case class CFinished()
}

object A {
  def props(): Props = Props(new A())
}

class A() extends Actor with ActorLogging {

  implicit val ec = context.dispatcher

  context actorOf B.props

  def receive = working

  def working: Receive = {
    case BFinished =>
      log.warning("[Actor B] -> Received: BFinished")
      context stop self
  }
}

object B {
  def props(): Props = Props(new B())
}

class B() extends Actor with ActorLogging {

  implicit val ec = context.dispatcher
  var cancellable: Cancellable = _

  context actorOf C.props

  def receive = working

  def working: Receive = {
    case CFinished =>
      log.warning("[Actor B] -> Received: CFinished")
      cancellable = scheduleEndActor
      context become ending
  }

  def ending: Receive = {
    case _ =>
  }

  def scheduleEndActor = context.system.scheduler.schedule(0.seconds, 5.seconds)(endActor())

  def endActor() = context.parent ! BFinished
}

object C {
  def props(): Props = Props(new C())
}

class C() extends Actor with ActorLogging {

  implicit val ec = context.dispatcher
  var cancellable: Cancellable = _

  self ! CFinished

  def receive = working

  def working: Receive = {
    case CFinished =>
      log.warning("[Actor C] -> Received: CFinished")
      cancellable = scheduleEndActor
      context become ending
  }

  def ending: Receive = {
    case _ =>
  }

  def scheduleEndActor = context.system.scheduler.schedule(0.seconds, 5.seconds)(endActor())

  def endActor() = context.parent ! CFinished
}

错误:

[WARN] [10/20/2016 00:05:59.700] [test-system-akka.actor.default-dispatcher-4] [akka://test-system/user/$a/$a/$a] [Actor C] -> Received: CFinished
[WARN] [10/20/2016 00:05:59.706] [test-system-akka.actor.default-dispatcher-2] [akka://test-system/user/$a/$a] [Actor B] -> Received: CFinished
[WARN] [10/20/2016 00:05:59.709] [test-system-akka.actor.default-dispatcher-3] [akka://test-system/user/$a] [Actor B] -> Received: BFinished
[ERROR] [10/20/2016 00:06:04.714] [test-system-akka.actor.default-dispatcher-8] [TaskInvocation] null
java.lang.NullPointerException
    at com.test.B.endActor(Test.scala:62)
    at com.test.B$$anonfun$scheduleEndActor$1.apply$mcV$sp(Test.scala:60)
    at akka.actor.Scheduler$$anon$2.run(Scheduler.scala:78)
    at akka.actor.LightArrayRevolverScheduler$$anon$2$$anon$1.run(LightArrayRevolverScheduler.scala:104)
    at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:39)
    at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:409)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

[ERROR] [10/20/2016 00:06:04.714] [test-system-akka.actor.default-dispatcher-2] [TaskInvocation] null
java.lang.NullPointerException
    at com.test.C.endActor(Test.scala:90)
    at com.test.C$$anonfun$scheduleEndActor$2.apply$mcV$sp(Test.scala:88)
    at akka.actor.Scheduler$$anon$2.run(Scheduler.scala:78)
    at akka.actor.LightArrayRevolverScheduler$$anon$2$$anon$1.run(LightArrayRevolverScheduler.scala:104)
    at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:39)
    at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:409)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

3 个答案:

答案 0 :(得分:2)

我刚刚意识到我可以阻止调度程序覆盖postStop方法。这样,NullPointerException就不会被抛出。

  override def postStop(): Unit = cancellable.cancel

答案 1 :(得分:2)

第一:如果初始延迟为零,为什么需要调度程序? 第二:如果你确实需要它,那么你可以使用context.system.scheduler.scheduleOnce(x.seconds, context.parent, BFinished )。这样,您根本不需要使用cancellable.cancel

答案 2 :(得分:2)

它会抛出NPE,因为您正在从另一个线程访问context.parent

使用

def scheduleEndActor = context.system.scheduler.schedule(0.seconds, 5.seconds, context.parent, BFinished)