Поток игнорирует первый ввод при использовании 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. Нажатие Enter во второй раз изменяет флаг на 1. Поток работает нормально, переключая значение flag при дальнейших нажатиях Enter. Почему это происходит? Что я сделал не так?

Похоже, проблема только с getch, так как когда я использую gets вместо getch, проблема исчезает. Но я не могу использовать gets, потому что хочу, чтобы пользователь нажимал одну клавишу без необходимости нажимать Enter после клавиши для ввода. Например, flag не должно изменяться, когда пользователь вводит a вместо Enter, поэтому я использовал getch, чтобы убедиться, что ввод осуществляется после одного нажатия на клавиатуру.

Аналогичная проблема была описана здесь, но это не дубликат.

Редактировать 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]

person Rishav    schedule 12.03.2018    source источник


Ответы (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 вызовет последовательность "\r\n" в binmode, но вижу только "\r". Не уверен, что происходит с "\n", но, похоже, так работает надежно. Также обратите внимание, что в текущей версии программу нельзя завершить с помощью Ctrl+C. Вам нужно будет добавить чек для этого.

person bambino350    schedule 15.03.2018
comment
Действительно помогло. ты - person Rishav; 16.03.2018