使用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 终止程序。您必须为此添加一张支票。
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 终止程序。您必须为此添加一张支票。