Scala 如何终止从SBT外壳启动的FS2流?

Scala 如何终止从SBT外壳启动的FS2流?,scala,fs2,cats-effect,Scala,Fs2,Cats Effect,如果我从SBTShell运行这个程序,然后取消它,它将继续打印“hello”。我必须退出SBT才能让它停下来。为什么呢 import cats.effect.{ExitCode, IO, IOApp} import fs2.Stream import scala.concurrent.duration._ object FS2 extends IOApp { override def run(args: List[String]) = Stream.awakeEvery[I

如果我从SBTShell运行这个程序,然后取消它,它将继续打印“hello”。我必须退出SBT才能让它停下来。为什么呢

import cats.effect.{ExitCode, IO, IOApp}
import fs2.Stream
import scala.concurrent.duration._

object FS2 extends IOApp {

  override def run(args: List[String]) = 
      Stream.awakeEvery[IO](5.seconds).map { _ =>
        println("hello")
      }.compile.drain.as(ExitCode.Error)
}

正如在评论中已经提到的,您的应用程序在另一个线程中运行,并且它永远不会终止,因为流是无限的,所以当应用程序收到SIGTERM或SIGINT之类的信号时,您必须手动终止它(每当您点击
ctrl+c
终止应用程序时,它就会发出)

你可以这样做:

  • 创建一个延迟的实例
  • 在接收到任何TERM或INT信号后,当发生中断时,使用它触发
  • 例如:

    object FS2 extends IOApp {
    
      override def run(args: List[String]): IO[ExitCode] = for {
        cancel <- Deferred[IO, Either[Throwable, Unit]] //deferred used as flat telling if terminations signal
                                                        //was received
        _ <- (IO.async[Unit]{ cb =>
          Signal.handle(
            new Signal("INT"), //INT and TERM signals are nearly identical, we have to handle both
            (sig: Signal) => cb(Right(()))
          )
          Signal.handle(
            new Signal("TERM"),
            (sig: Signal) => cb(Right(()))
          )
        } *> cancel.complete(Right(()))).start //after INT or TERM signal is intercepted it will complete
                                               //deferred and terminate fiber
                                               //we have to run method start to run waiting for signal in another fiber
                                               //in other case program will block here
        app <- Stream.awakeEvery[IO](1.seconds).map { _ => //your stream
          println("hello")
        }.interruptWhen(cancel).compile.drain.as(ExitCode.Error)  //interruptWhen ends stream when deferred completes
      } yield app
    
    }
    
    对象FS2扩展IOApp{
    覆盖def运行(args:List[String]):IO[ExitCode]=for{
    取消cb(右(()))
    )
    信号手柄(
    新信号(“术语”),
    (sig:Signal)=>cb(右(())
    )
    }*>cancel.complete(右(()))。start//截获INT或TERM信号后,它将完成
    //延迟和终止光纤
    //我们必须运行方法开始运行,等待另一个光纤中的信号
    //在其他情况下,程序将在此阻塞
    应用程序//您的流
    println(“你好”)
    }.interruptWhen(cancel).compile.drain.as(ExitCode.Error)//interruptWhen在延迟完成时结束流
    }收益应用程序
    }
    

    只要您在sbt shell中点击
    ctrl+c
    ,此版本的应用程序就会终止。

    您需要开始运行。谢谢!这很有效,但我不明白。为什么非fs2应用程序在(true){println(“hello”)}
    时不执行
    ?fs2应用程序运行在不同的线程上,因此当您终止启动它的线程时,您没有终止它运行的线程。分叉时,您会杀死整个JVM,在这种情况下,fs2也会注册一个清理操作,以便在关机时激活。使用
    interruptWhen
    ,并在关机挂钩中或在应用程序退出之前的某个位置设置停止信号。