流量:
A
,B
和C
)。 A
创建B
,B
创建C
。 A --> B --> C
B
和C
有两种状态:working
和ending
。在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)
答案 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)