如何取消 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
才能使用你的调用版本:
tools.jline.AnsiWindowsTerminal
并使用采用 Terminal
的 ConsoleReader
构造函数(否则 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 请求