Python3:异步检测按键并将其传达给主线程

时间:2019-06-11 04:01:02

标签: python python-3.x multithreading keyboard stdin

免责声明:导入语句在函数内,我知道这很罕见。我在这里展示我的整个程序,是在讲述我的问题和想法的同时,逐个功能地展示它。实际上,我在做一些不同的事情,我只是为这个Stackoverflow问题做了一个最小的例子。有重复的问题,但是我在其中找不到很好的答案,因为那些问题只说“使用多线程”(例如this answer)。这个特定的问题与如何使用多线程有关。

故事:我正在用Python运行程序。假设这是一个进入无限循环的while循环。它只是运行愉快。例如,

def job(threadname, q):
  from time import sleep
  c = 0
  while True:
    sleep(0.1) #slow the loop down 
    c += 1
    print(c)

我想做的是,它异步检测到stdin上的按键,然后中断执行,这样我就可以在被中断的函数中执行我想做的任何事情(或者如果我正在运行)它与python3 -i program.py一起使用,以便在装入我所有模块的情况下切换到REPL,请记住,这是一个最小的示例,在该示例中,我不想过多强调此类问题。

我的想法是:我有一个函数可以异步获取按键,然后通过队列将其发送到另一个线程,然后它可以工作。因此,我将工作职能扩展为:

def job(threadname, q):
  from time import sleep
  c = 0
  while True:
    sleep(0.1) #slow the loop down 
    c += 1
    print(c)
    ch = q.get() #extension for multithreading
    handle_keypress(ch) #extension for handling keypresses

handle_keypress(ch)的代码是:

def handle_keypress(key):
  if (key == "q"):
    print("Quit thread")
    exit(0)
  elif (key == "s"):
    print("would you like to change the step size? This has not been implemented yet.")
  else:
    print("you pressed another key, how nice! Unfortunately, there are not anymore options available yet.")

换句话说,除了展示我希望能够做到这一点之外,没什么有趣的。

起初,问题似乎出在job()函数中。罪魁祸首是q.get(),它正在悬挂。但是,它挂起是因为我的输入线程出于某种原因不是异步的并且阻塞了。我不知道如何使其畅通无阻。

这是我的输入线程的功能:

def get_input(threadname, q):
  #get one character, this code is adapted from https://stackoverflow.com/questions/510357/python-read-a-single-character-from-the-user
  while True:
    import sys, tty, termios
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        tty.setraw(sys.stdin.fileno())
        ch = sys.stdin.read(1)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    q.put(ch)

对我来说,sys.stdin.read(1)很明显是受阻的,但我不知道如何使其不受阻。在现在的状态下,我也想不出一种解决毒丸问题的方法,这就是q.get()函数中job()被阻止的原因。

我通过调用以下函数来运行程序:

def run_program():
  from threading import Thread
  from queue import Queue
  queue = Queue()
  thread1 = Thread( target=get_input, args=("Thread-1", queue) )
  thread2 = Thread( target=job, args=("Thread-2", queue) )

  thread1.start()
  thread2.start()
  thread1.join()
  thread2.join()

我的问题:这是您如何设计用于处理异步按键的程序?如果是这样,如何使get_input()函数不受阻碍?

1 个答案:

答案 0 :(得分:0)

多亏了Sav,我找到了回答这个问题的方法。我认为,他的评论就是答案。因此,如果他将重写他的评论。我接受他的回答。现在,我将展示为了使非阻塞实现正常工作而更改的代码部分:

def job(threadname, q):
  from queue import Empty
  from time import sleep
  c = 0
  while True:
    sleep(0.1) #slow the loop down 
    c += 1
    print(c)
    #Below is the changed part
    ch = None
    try:
      ch = q.get(block=False)
    except Empty:
      pass
    if ch is not None:
      handle_keypress(ch)