并发/非阻塞控制台键盘输入

Posted

技术标签:

【中文标题】并发/非阻塞控制台键盘输入【英文标题】:Concurrent/Non-blocking console keyboard input 【发布时间】:2011-01-16 00:29:36 【问题描述】:

我正在用 java 开发一个 MUD。我每次都读取玩家输入,但我使用的是使用阻塞操作的Scanner。我想要非阻塞输入。

我查看了nio 包,它有一个Selector 类,但我不确定如何使用它来处理System.in。我想我在运行服务器后肯定会需要它,但现在一切都处于离线状态。

我尝试从Applet 扩展主类并覆盖keyDown,但这只是意味着在第一个之后不再接受输入。当然,我不再阻止任何东西,但是没有更多的输入。我猜keyDown 再也没有接到过电话。

也许线程即使在执行阻塞操作时也会被中断?

感谢您对此问题的任何见解。

【问题讨论】:

非阻塞套接字(网络)的完成方式与非阻塞 IO 不同,至少在大多数环境中不同。 【参考方案1】:

你不能用系统控制台来做这件事,因为现在它不能用多平台的方式来做。

您可以将摇摆窗口用作控制台或查找基于 JNI 的方法,但它可能不适用于某些平台。

您可以使用JCurses。它可能会工作,它基于 JNI 并支持 Windows 和 Linux。

【讨论】:

JCurses 是在 UNIX curses 上构建的,所以它肯定可以工作。 截至 2014 年 5 月 23 日,explanation 链接已失效(现在链接到域停放页面)。 @daveloyall 我删除了它。谢谢。【参考方案2】:

keyDown() 是deprecated,所以我建议改用processKeyEvent 和keyListener。

也许线程在执行阻塞操作时也会被中断?

是的,如果您有要中断的线程对象的引用,您可以简单地在该实例上调用interrupt() 方法。并且在线程的run方法中你可以处理被中断的异常。但是,这似乎有点hack-ish。我看不出这比使用简单的 KeyListener 更有帮助。

【讨论】:

Thread.interrupt() 仅设置中断标志并在线程处于睡眠或阻塞状态时唤醒它。它不会中断套接字读取的常规阻塞文件 IO。它也不会中断繁重的计算或自旋锁 - 您的代码需要检查并遵守“中断”标志才能被中断。【参考方案3】:

JLine 也可能适合您:

http://jline.sourceforge.net/apidocs/src-html/jline/ConsoleReader.html#line.1447

【讨论】:

downvote:以上链接指向ConsoleReader.readVirtualKey,这是一个阻塞操作。【参考方案4】:

我不得不通过阻止从 http 写入/读取来解决类似的问题。在那种特殊情况下,我使用了本地缓冲区和线程。

想法很简单,一个线程从标准输入读取并将内容放入缓冲区。第二做同样的写作。

然后您在缓冲区中使用非阻塞查询。

示例代码:

class NonBlockingReader implements Runnable
  Reader in;
  List buffer;
  public void run()
    String line=null;
    while((line=in.readLine())!=null)
      storeLine(line);
    
  
  private synchronized storeLine(String line)
    buffer.add(line);
  
  public synchronized String getLine()
    if(list.size()>0)
       return list.removeFirst();
    return null;
  


// .. same for writer, then you jast pass stdin and stdout ...

【讨论】:

System.in 流是 Java 的标准输入等效项被缓冲,因此应用程序在用户按下回车之前看不到字符。

以上是关于并发/非阻塞控制台键盘输入的主要内容,如果未能解决你的问题,请参考以下文章

在控制台 C++ 中获取鼠标和键盘输入

C 非阻塞键盘输入

键盘敲击(keyboard hit)

windows下C语言非阻塞方式读取键盘缓冲区

3.6.2.阻塞式IO的困境

kbhit()