如何取消ConsoleReader.readLine()

发布于 2024-11-24 22:23:47 字数 1006 浏览 1 评论 0原文

首先,我正在学习scala,并且是java世界的新手。 我想创建一个控制台并将该控制台作为可以启动和停止的服务运行。 我能够在 Actor 中运行 ConsoleReader,但我不知道如何正确停止 ConsoleReader。 这是代码:

import eu.badmood.util.trace
import scala.actors.Actor._

import tools.jline.console.ConsoleReader

object Main {

  def main(args:Array[String]){
    //start the console
    Console.start(message => {
      //handle console inputs
      message match {
        case "exit" => Console.stop()
        case _ => trace(message)
      }
    })

    //try to stop the console after a time delay
    Thread.sleep(2000)
    Console.stop()

  }

}

object Console {

  private val consoleReader = new ConsoleReader()

  private var running = false

  def start(handler:(String)=>Unit){
    running = true
    actor{
      while (running){
        handler(consoleReader.readLine("\33[32m> \33[0m"))
      }
    }
  }

  def stop(){
    //how to cancel an active call to ConsoleReader.readLine ?
    running = false
  }

}

我也在寻找有关此代码的任何建议!

first of all, i'm learning scala and new to the java world.
I want to create a console and run this console as a service that you could start and stop.
I was able to run a ConsoleReader into an Actor but i don't know how to stop properly the ConsoleReader.
Here is the code :

import eu.badmood.util.trace
import scala.actors.Actor._

import tools.jline.console.ConsoleReader

object Main {

  def main(args:Array[String]){
    //start the console
    Console.start(message => {
      //handle console inputs
      message match {
        case "exit" => Console.stop()
        case _ => trace(message)
      }
    })

    //try to stop the console after a time delay
    Thread.sleep(2000)
    Console.stop()

  }

}

object Console {

  private val consoleReader = new ConsoleReader()

  private var running = false

  def start(handler:(String)=>Unit){
    running = true
    actor{
      while (running){
        handler(consoleReader.readLine("\33[32m> \33[0m"))
      }
    }
  }

  def stop(){
    //how to cancel an active call to ConsoleReader.readLine ?
    running = false
  }

}

I'm also looking for any advice concerning this code !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风筝在阴天搁浅。 2024-12-01 22:23:47

从输入读取字符的底层调用是阻塞的。在非 Windows 平台上,它将使用 System.in.read() ,在 Windows 上它将使用 org.fusesource.jansi.internal.WindowsSupport.readByte 。

因此,您面临的挑战是当您想要停止控制台服务时导致阻塞调用返回。请参阅 http://www.javaspecialists.eu/archive/Issue153.html 和 < a href="https://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-java-inputstream-with-a-timeout">是否可能从具有超时的 InputStream 读取? 一些想法...一旦你弄清楚了,当你的控制台服务停止时,让 read 返回 -1,这样 ConsoleReader 就认为已经完成了。您需要 ConsoleReader 才能使用您的该调用版本:

  • 如果您使用的是 Windows,则可能需要覆盖 tools.jline.AnsiWindowsTerminal 并使用 ConsoleReader 构造函数需要一个 Terminal (否则 AnsiWindowsTerminal 将直接使用 WindowsSupport.readByte`)
  • 在 unix 上,有一个带有 InputStreamConsoleReader 构造函数,您可以为 System.in 提供自己的包装器

还有一些想法:

  • 有一个 scala .Console 对象已经存在,因此为了减少混乱,请以不同的方式命名您的对象。
  • System.in 是一种独特的资源,因此您可能需要确保一次只有一个调用者使用 Console.readLine。现在start将直接调用readLine并且多个调用者可以调用start。也许控制台服务可以readLine并维护处理程序列表。

The underlying call to read a characters from the input is blocking. On non-Windows platform, it will use System.in.read() and on Windows it will use org.fusesource.jansi.internal.WindowsSupport.readByte.

So your challenge is to cause that blocking call to return when you want to stop your console service. See http://www.javaspecialists.eu/archive/Issue153.html and Is it possible to read from a InputStream with a timeout? for some ideas... Once you figure that out, have read return -1 when your console service stops, so that ConsoleReader thinks it's done. You'll need ConsoleReader to use your version of that call:

  • If you are on Windows, you'll probably need to override tools.jline.AnsiWindowsTerminal and use the ConsoleReader constructor that takes a Terminal (otherwise AnsiWindowsTerminal will just use WindowsSupport.readByte` directly)
  • On unix, there is one ConsoleReader constructor that takes an InputStream, you could provide your own wrapper around System.in

A few more thoughts:

  • There is a scala.Console object already, so for less confusion name yours differently.
  • System.in is a unique resource, so you probably need to ensure that only one caller uses Console.readLine at a time. Right now start will directly call readLine and multiple callers can call start. Probably the console service can readLine and maintain a list of handlers.
霓裳挽歌倾城醉 2024-12-01 22:23:47

假设 ConsoleReader.readLine 响应线程中断,您可以重写 Console 以使用一个线程,然后您可以中断该线程来停止它。

object Console {

  private val consoleReader = new ConsoleReader()
  private var thread : Thread = _

  def start(handler:(String)=>Unit) : Thread = {
    thread = new Thread(new Runnable {
      override def run() {
        try {
          while (true) {
            handler(consoleReader.readLine("\33[32m> \33[0m"))
          }
        } catch {
          case ie: InterruptedException =>
        }
      }
    })
    thread.start()
    thread
  }

  def stop() {
    thread.interrupt()
  }

}

Assuming that ConsoleReader.readLine responds to thread interruption, you could rewrite Console to use a Thread which you could then interrupt to stop it.

object Console {

  private val consoleReader = new ConsoleReader()
  private var thread : Thread = _

  def start(handler:(String)=>Unit) : Thread = {
    thread = new Thread(new Runnable {
      override def run() {
        try {
          while (true) {
            handler(consoleReader.readLine("\33[32m> \33[0m"))
          }
        } catch {
          case ie: InterruptedException =>
        }
      }
    })
    thread.start()
    thread
  }

  def stop() {
    thread.interrupt()
  }

}
甜尕妞 2024-12-01 22:23:47

您可以覆盖您的 ConsoleReader 输入流。恕我直言,这是合理的,因为 STDIN 是一个“慢”流。请根据您的需要改进示例。这只是草图,但它可以工作:

def createReader() =
terminal.synchronized {
  val reader = new ConsoleReader
  terminal.enableEcho()
  reader.setBellEnabled(false)
  reader.setInput(new InputStreamWrapper(reader.getInput())) // turn on InterruptedException for InputStream.read
  reader
}

使用 InputStream 包装器:

class InputStreamWrapper(is: InputStream, val timeout: Long = 50) extends FilterInputStream(is) {
@tailrec
final override def read(): Int = {
  if (is.available() != 0)
    is.read()
  else {
    Thread.sleep(timeout)
    read()
  }
}

}

PS 我尝试使用 NIO - System.in 有很多麻烦(尤其是跨平台)。我又回到了这个变体。 CPU 负载接近 0%。这适合这种交互式应用。

You may overwrite your ConsoleReader InputStream. IMHO this is reasonable well because of STDIN is a "slow" stream. Please improve example for your needs. This is only sketch, but it works:

def createReader() =
terminal.synchronized {
  val reader = new ConsoleReader
  terminal.enableEcho()
  reader.setBellEnabled(false)
  reader.setInput(new InputStreamWrapper(reader.getInput())) // turn on InterruptedException for InputStream.read
  reader
}

with InputStream wrapper:

class InputStreamWrapper(is: InputStream, val timeout: Long = 50) extends FilterInputStream(is) {
@tailrec
final override def read(): Int = {
  if (is.available() != 0)
    is.read()
  else {
    Thread.sleep(timeout)
    read()
  }
}

}

P.S. I tried to use NIO - a lot of troubles with System.in (especially crossplatform). I returned to this variant. CPU load is near 0%. This is suitable for such interactive application.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文