使用 race from cats-effect 阻止应用程序退出
Using race from cats-effect prevents app from exiting
我有简单的猫效果应用程序,它从作为参数给出的 URL 下载站点。在下载过程中,应用程序应该通过将点 (.
) 写入控制台来显示 "loading bar"。我通过执行 race 两个 IOs 来实现它,一个用于下载另一个用于显示点。
这是 scastie 上的整个应用程序。
最重要的部分在这里:
def loader(): IO[Unit] = for {
_ <- console.putStr(".")
_ <- timer.sleep(Duration(50, MILLISECONDS)) *> loader()
} yield {}
def download(url: String): IO[String] = IO.delay(Source.fromURL(url)).map(_.mkString)
def run(args: List[String]): IO[Unit] = {
args.headOption match {
case Some(url) =>
for {
content <- IO.race(download(url), loader()).map(_.left.get)
_ <- console.putStrLn() *> console.putStrLn(s"Downloaded site from $url. Size of downloaded content is ${content.length}.")
} yield {}
case None => console.putStrLn("Pass url as argument.")
}
}
一切如我所料,当我 运行 它时,我得到:
..............
Downloaded site from https://www.scala-lang.org. Size of downloaded content is 47738.
唯一的问题是应用程序永远不会退出。
据我检查,loader IO 被正确取消。我什至可以添加这样的内容:
urlLoader.run(args) *> console.putStrLn("???") *> IO(ExitCode.Success)
并显示 ???
。
此外,当我删除 race 时,应用程序会正确退出。
所以我的问题是如何解决这个问题并让我的应用程序最后退出?
继续我上面的评论:问题是您的 ScheduledExecutorService
有线程 运行 阻止 JVM 退出,即使您的计时器任务已被取消。有几种方法可以解决此问题:
- 在
IO(ExitCode.Success)
前加一个IO(ses.shutdown())
。
- 使用线程工厂调用
newScheduledThreadPool
,线程工厂对其线程进行守护进程。
- 使用您在
IOApp
中免费获得的 timer: Timer
。
最后一个几乎肯定是正确的选择——使用 IOApp
提供的计时器(和 ContextShift
)将为您提供合理的默认行为和其他行为。
我有简单的猫效果应用程序,它从作为参数给出的 URL 下载站点。在下载过程中,应用程序应该通过将点 (.
) 写入控制台来显示 "loading bar"。我通过执行 race 两个 IOs 来实现它,一个用于下载另一个用于显示点。
这是 scastie 上的整个应用程序。
最重要的部分在这里:
def loader(): IO[Unit] = for {
_ <- console.putStr(".")
_ <- timer.sleep(Duration(50, MILLISECONDS)) *> loader()
} yield {}
def download(url: String): IO[String] = IO.delay(Source.fromURL(url)).map(_.mkString)
def run(args: List[String]): IO[Unit] = {
args.headOption match {
case Some(url) =>
for {
content <- IO.race(download(url), loader()).map(_.left.get)
_ <- console.putStrLn() *> console.putStrLn(s"Downloaded site from $url. Size of downloaded content is ${content.length}.")
} yield {}
case None => console.putStrLn("Pass url as argument.")
}
}
一切如我所料,当我 运行 它时,我得到:
.............. Downloaded site from https://www.scala-lang.org. Size of downloaded content is 47738.
唯一的问题是应用程序永远不会退出。
据我检查,loader IO 被正确取消。我什至可以添加这样的内容:
urlLoader.run(args) *> console.putStrLn("???") *> IO(ExitCode.Success)
并显示 ???
。
此外,当我删除 race 时,应用程序会正确退出。
所以我的问题是如何解决这个问题并让我的应用程序最后退出?
继续我上面的评论:问题是您的 ScheduledExecutorService
有线程 运行 阻止 JVM 退出,即使您的计时器任务已被取消。有几种方法可以解决此问题:
- 在
IO(ExitCode.Success)
前加一个IO(ses.shutdown())
。 - 使用线程工厂调用
newScheduledThreadPool
,线程工厂对其线程进行守护进程。 - 使用您在
IOApp
中免费获得的timer: Timer
。
最后一个几乎肯定是正确的选择——使用 IOApp
提供的计时器(和 ContextShift
)将为您提供合理的默认行为和其他行为。