Python (2.7) raw_input не отвечает на ввод пользователя, когда работает другой поток - PullRequest
0 голосов
/ 01 мая 2018

У меня есть небольшой сервер Python для следующих задач:

  • Основной поток получает пользовательский ввод с использованием raw_input;
  • Другой поток (то есть фоновый поток) запускается основным потоком для выполнения заданий, соответствующих пользовательскому вводу. Все задания пишутся как bash-скрипт, поэтому этот поток использует subprocess.Popen для запуска скрипта.

Код высокого уровня выглядит следующим образом (просто абстрагируйте его, потому что подробный код слишком длинный, чтобы копировать его здесь ...):

# A global queue is initialized to cache user inputs
command_queue = Queue.Queue()

# This is the background-thread running bash script based on user's input
class command_runner(Thread):

  def run(self):
    user_command = command_queue.get()
    # code to run bash script specified in user_command, using Popen

class main_program(object):

  def listen_user_input(self):
    command_runner.start()
    while True:
      user_input = raw_input("Please input command:")
      command_queue.put(user_input)

Что я заметил, так это то, что когда фоновый поток (command_runner) запускает некоторый bash-скрипт, raw_input основной программы иногда не отвечает ни на какой пользовательский ввод. Кажется, что raw_input застрял.

Некоторая другая информация, которая может быть полезна:

  • Сценарии bash, запускаемые command_running, являются долгосрочными, но не тяжелыми (обычно ~ 10 минут). Поэтому, когда параметр raw_input застрял, на машине не хватает ресурсов.
  • Некоторые сценарии могут использовать ssh на других компьютерах, используя «ssh -q some_command 2> & 1» (поэтому команда ssh выполняется тихо).
  • Все stdout и stderr скриптов bash перенаправляются в файл через «Popen (user_command, stderr = sys.stdout.fileno (), stdout = log_fp)»

1 Ответ

0 голосов
/ 01 мая 2018

Попробуйте добавить stdin=subprocess.PIPE к вашему Popen вызову:

Popen(..., stdin=subprocess.PIPE, ...)

По умолчанию Popen наследует файловые дескрипторы своего родителя , включая stdin. Вы явно даете ему новые stdout и stderr, но не новый stdin, который он поэтому наследует от вашего основного процесса Python. Я предполагаю, что stdin захватывается дочерним процессом (даже если этот процесс не нуждается в этом), предотвращая ваш вызов на raw_input от получения ввода и возврата до тех пор, пока процесс Popen не завершится и не вернет управление stdin.

Если процесс может попытаться прочитать из stdin, и вы не планируете что-либо записывать в этот канал, вы также можете сделать так, чтобы он рисовал из /dev/null (в linux), используя что-то вроде:

Popen(..., stdin=open('/dev/null', 'rb').fileno(), ...)

Это позволило бы (не) читать со стандартного ввода без зависания.

...