如何取消 ConsoleReader.readLine()

Posted

技术标签:

【中文标题】如何取消 ConsoleReader.readLine()【英文标题】:how to cancel ConsoleReader.readLine() 【发布时间】:2011-10-07 01:45:48 【问题描述】:

首先,我正在学习 scala 和 Java 世界的新手。 我想创建一个控制台并将此控制台作为可以启动和停止的服务运行。 我能够将 ConsoleReader 运行到 Actor 中,但我不知道如何正确停止 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
  


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

【问题讨论】:

我不确定关闭控制台意味着什么。为什么需要“关闭”它? 如果我直接调用 stop 方法(不是在处理控制台输入之后),ConsoleReader 仍将等待输入。我想知道是否可以取消 readLine 调用 【参考方案1】:

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

因此,当您想要停止控制台服务时,您面临的挑战是让该阻塞调用返回。请参阅http://www.javaspecialists.eu/archive/Issue153.html 和Is it possible to read from a InputStream with a timeout? 了解一些想法......一旦你弄清楚了,当你的控制台服务停止时让read 返回-1,这样ConsoleReader 就认为它已经完成了。你需要ConsoleReader 才能使用你的调用版本:

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

还有一些想法:

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

【讨论】:

哎哟!我认为这将不那么困难!我还有很多东西要学:)谢谢你的回答! @OXMO456,如果您不关心在程序终止之前剩余的控制台提示,您可以做的更简单:只需在它自己的线程中启动ConsoleReader并使用thread.setDaemon(true),这样它就不会' t 防止程序终止。 谢谢!在更好地理解基础知识之前,我将把它放在一边!【参考方案2】:

假设 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()
  


【讨论】:

您好,感谢您的回复。我刚刚尝试了您的代码,但线程似乎没有中断。只是在执行期间收到警告: Failed to query stty columnsjava.lang.InterruptedException at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:485) ... consoleReader.readLine 可能不响应中断。然而,大多数 Java 标准库的阻塞方法都可以,尤其是在 IO 方面。实际上,我只是检查了 javadocs,看起来它没有抛出 InterruptedException 通常当一个 IO 方法阻塞但不可中断时,您可以绕过这个问题,而不是调用 thread.interrupt() 来停止它,您可以关闭输入流或它正在使用的东西, 这通常会抛出一个你可以捕获的 IOException。 我试图关闭输入流:consoleReader.getInput().close() 但提示仍然在这里:(按回车后出现错误...我明天再试一次【参考方案3】:

您可以覆盖您的 ConsoleReader InputStream。恕我直言,这是合理的,因为 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()
  

附:我尝试使用 NIO - System.in 有很多麻烦(尤其是跨平台)。我回到了这个变种。 CPU 负载接近 0%。这适用于这样的交互式应用程序。

【讨论】:

是的。 API 已更改...自 2012 年起

以上是关于如何取消 ConsoleReader.readLine()的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Angular 4+ 中取消/取消订阅所有挂起的 HTTP 请求

如何取消2003系统关机计划

如何取消mySql 5.0 密码

如何取消WORD中的自动更新时间

如何使用取消令牌Azure Service Bus(SubscriptionClient)

如何根据条件行值对 pandas 数据框进行取消堆叠或取消透视?