终止 Actor 和 ActorSystem 后应用程序不退出
Application not exiting after terminating Actor and ActorSystem
我有一个主程序,它创建一个 ActorSystem,一个 Actor 并向 actor 发送一些消息。处理完消息后,我发送了 PoisonPill 来杀死 Actor。然后我关闭了actor系统。
在 Actor 中,我调用 Await 等待未来完成。我面临的问题是应用程序没有退出,即使 actor 被 PoisonPill 终止并且 ActorSystem 已关闭。
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem("sytem")
val creationActor = actorSystem.actorOf(Props[MyActor], "MyActor")
...
creationActor ! Message //may be called multiple times
creationActor ! PoisonPill
...
}
演员代码是
class MyActor extends Actor {
override def receive: Receive = {
case Message => {
...
Await.result(Dataset.create(datasetId), 30 seconds)
//Dataset.create returns a Future. Also this method uses an
//ExecutionContext of its own.
...
}
}
override def postStop() = {
context.system.shutdown()
}
}
如果我注释掉 Await.result 部分,程序将退出。
编辑:
看来我找到了根本原因。
Dataset.create(…) 中使用的 ExecutionContext 是罪魁祸首。当我使用不使用 Futures 的同步版本 Dataset.create(…) 时,我的应用程序退出。
Dataset.create()使用的ec是这样定义的
implicit val defaultContext =
ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(100))
还是很想知道为什么异步版本不退出应用
编辑 2:
如果我将 Await 部分更改为
val future = BQDataset.create(datasetId)
future onComplete {
case Success(d) => ...
case Failure(e) => ...
}
我仍然面临同样的问题。
正如@cem-catikkas 提到的,当我调用 BQDataset.create
时创建的 ExecutionContext 正在运行。我使用 jstack 验证了这一点,可以看到 "pool-1-thread-1" prio=5 tid=0x00007ff49aa1e800 nid=0x4e03 waiting
actor 是单线程的,一次处理一条消息。 Await 是一个阻塞操作。听起来演员正在忙着CostlyOp
。毒丸不会打断它,所以 actor 不会关闭,直到它处理完毒丸前面的所有代价高昂的操作。
当我调用 Dataset.create(…)
时,该方法会创建一个 ExecutionContext
并使用它来执行第三方库。此第三方库打开网络连接,但没有正确的 shutdown()
或 close()
方法来清理底层连接。
因此,即使我调用 ExecutionContext.shutdown()
,它也无法关闭线程池。这就是应用程序没有退出的原因。
我有一个主程序,它创建一个 ActorSystem,一个 Actor 并向 actor 发送一些消息。处理完消息后,我发送了 PoisonPill 来杀死 Actor。然后我关闭了actor系统。
在 Actor 中,我调用 Await 等待未来完成。我面临的问题是应用程序没有退出,即使 actor 被 PoisonPill 终止并且 ActorSystem 已关闭。
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem("sytem")
val creationActor = actorSystem.actorOf(Props[MyActor], "MyActor")
...
creationActor ! Message //may be called multiple times
creationActor ! PoisonPill
...
}
演员代码是
class MyActor extends Actor {
override def receive: Receive = {
case Message => {
...
Await.result(Dataset.create(datasetId), 30 seconds)
//Dataset.create returns a Future. Also this method uses an
//ExecutionContext of its own.
...
}
}
override def postStop() = {
context.system.shutdown()
}
}
如果我注释掉 Await.result 部分,程序将退出。
编辑:
看来我找到了根本原因。
Dataset.create(…) 中使用的 ExecutionContext 是罪魁祸首。当我使用不使用 Futures 的同步版本 Dataset.create(…) 时,我的应用程序退出。
Dataset.create()使用的ec是这样定义的
implicit val defaultContext =
ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(100))
还是很想知道为什么异步版本不退出应用
编辑 2: 如果我将 Await 部分更改为
val future = BQDataset.create(datasetId)
future onComplete {
case Success(d) => ...
case Failure(e) => ...
}
我仍然面临同样的问题。
正如@cem-catikkas 提到的,当我调用 BQDataset.create
时创建的 ExecutionContext 正在运行。我使用 jstack 验证了这一点,可以看到 "pool-1-thread-1" prio=5 tid=0x00007ff49aa1e800 nid=0x4e03 waiting
actor 是单线程的,一次处理一条消息。 Await 是一个阻塞操作。听起来演员正在忙着CostlyOp
。毒丸不会打断它,所以 actor 不会关闭,直到它处理完毒丸前面的所有代价高昂的操作。
当我调用 Dataset.create(…)
时,该方法会创建一个 ExecutionContext
并使用它来执行第三方库。此第三方库打开网络连接,但没有正确的 shutdown()
或 close()
方法来清理底层连接。
因此,即使我调用 ExecutionContext.shutdown()
,它也无法关闭线程池。这就是应用程序没有退出的原因。