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

时间:2018-03-12 09:13:20

标签: ruby multithreading stdin 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的值。但是线程不会更改{strong>第一个 Enter 我点击的flag的值。点击输入,第二次将标志更改为1。该线程通常在进一步 Enter 命中时切换flag的值。为什么会这样?我做错了什么?

问题似乎仅适用于getch 就像当我使用gets代替getch时,问题就消失了。但是我不能使用gets,因为我希望用户只需按下 Enter 键即可点击一个键来输入。 例如,当用户输入 a 而不是 Enter 时,flag不应该更改,因此我使用getch确保输入是在单击键盘。

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

编辑1: 问题似乎是getch,而不是检查是什么。

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]

1 个答案:

答案 0 :(得分:1)

我在Windows机器上尝试了您的代码,并且能够重新创建问题。正如你所猜测的那样,它与线程无关,以及getch如何工作(在Windows上)。 如果你在你的循环中添加一个p temp.inspect,你会发现它并不是第一个'\ n'被吞下,而是它以某种方式被“阻止”。如果您按 Enter 和另一个键,最好的方法是看到这个。您将看到检查是“一个接一个”。

这里讨论关于这个问题的一个很好的解释: 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 终止。你必须为此添加一张支票。