使用getch时线程忽略第一个输入

Thread ignoring first input when using getch

require 'rubygems'
require 'mechanize'
require 'io/console'

flag = 0
t2 =Thread.new do
    puts flag
    loop do
        temp = STDIN.getch
        if temp=="\n"
            flag = (flag+1)%2
            puts flag
        end
    end
end

# => Some foreground code

t2.join

当我 运行 代码时,我得到 flag 的值,它应该打印 0 。但是线程不改变flag的值就回车我打了。不过,第二次按 Enter 会将标志更改为 1。该线程正常工作,在进一步 Enter 命中时切换 flag 的值。为什么会这样?我做错了什么?

问题似乎只有 getch 当我使用 gets 代替 getch 时,问题就消失了。但我不能使用 gets,因为我希望用户按一个键而不需要在键后按 Enter 来输入。 例如,当用户输入 a 而不是 Enter 时,flag 不应更改,因此我使用 getch 来制作确保输入是在单击键盘后给出的。

描述了一个类似的问题 here 但它不是重复的。

编辑 1: 问题似乎出在 getch 而不是 check what do ever.

flag = 0

t2 =Thread.new do
  puts flag

  loop do
    temp = STDIN.getch
    flag = (flag+1)%2
    puts flag
  end

end
t2.join

即使删除了 if 语句,第一个 Enter 无论如何都会被忽略,但其他字符似乎是第一次响应。只有当我按下 Enter 时,问题才会出现。不算我打的第一个Enter

ruby 2.3.3p222 (2016-11-21 revision 56859) [x64-mingw32]

我在 Windows 机器上试过你的代码,re-create 解决了这个问题。正如您猜对的那样,它与线程无关,也与 getch 的工作方式无关(在 Windows 上)。 如果将 p temp.inspect 添加到循环中,您会发现第一个 '\n' 并没有被吞掉,而是不知何故 "held back"。如果您交替按 Enter 和另一个键,查看此内容的最佳方式。你会看到 inspect 是 "off-by-one".

关于这个问题的一个很好的解释在这里讨论: https://www.rubytapas.com/2016/12/14/ruby-code-on-windows/

有了这些信息,一个简单的解决方案(在 Linux 上也对 运行 有额外的好处,不确定 Mac)是:

require 'rubygems'
require 'mechanize'
require 'io/console'

STDIN.binmode     #this line added to prevent line-end translation

flag = 0
t2 =Thread.new do
    puts flag
    loop do
        temp = STDIN.getch
        if temp=="\r"  # note: changed from LF to CR
            flag = (flag+1)%2
            puts flag
        end
    end
end

# => Some foreground code

t2.join

备注: 不可否认,我并不完全了解它的工作方式。我原以为 Enter 会在 binmode 中导致“\r\n”序列,但我只看到“\r”。不确定“\n”发生了什么,但它似乎以这种方式可靠地工作。 另请注意,在当前版本中,无法使用 Ctrl+C 终止程序。您必须为此添加一张支票。