我有一个主程序,它创建一个 ActorSystem,一个 Actor 并向 Actor 发送一些消息。处理消息后,我发送 PoisonPill 来杀死 Actor。然后我关闭了 Actor 系统。
在 Actor 中,我正在调用 Await 以等待 future 完成。我面临的问题是,即使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
...
}
而 Actor 代码是
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
最佳答案
当我打电话时Dataset.create(…)
,该方法创建一个 ExecutionContext
并使用它来执行第三方库。这个第三方库打开了一个网络连接,但它没有正确的 shutdown()
或 close()
清除底层连接的方法。
正因为如此,即使我打电话ExecutionContext.shutdown()
,它无法关闭线程池。这就是应用程序没有退出的原因。
关于scala - 终止 Actor 和 ActorSystem 后应用程序未退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27834200/