你如何终止/中断/中止Python console / sys.stdin readline()?

时间:2017-08-04 21:04:23

标签: python multithreading python-2.7 tkinter

在线程中,我有一个从用户控制台读取输入的循环。主线程忙于Tkinter mainloop()。如何终止此程序?

while True:
    ln = sys.stdin.readline()
    try:
        ln = ln[:-1]  # Remove LF from line
        if len(ln)==0: continue  # Ignore blank lines
        ...and so on
     

主要的thead调用startGUI(),其中包含一个tk.mainloop()调用。当我按下窗口上的X关闭按钮(这是Linux)时,Tkinter关闭窗口并返回mainloop()。然后我尝试关闭stdin,希望sys.stdin关闭并导致sys.stdin.readline()以一个好的EOF终止,允许我的stdinLoop线程终止。

# Start up the GUI window
startGUI()  # Doesn't return until GUI window is closed, tk.mainloop is called here
#
# Wait for stdinLoop thread to finish
sys.stdin.close()  # Hopefully cause stdinTh to close
print("waiting for stdinTh to join")
stdinTh.join()
print("joined stdinTh")

在sys.stdin.close()之后,sys.stdin.realine()永远不会返回。 (stdinTh.join()用于同步结束。)

我认为Python readline()正在做一些聪明的(在名为NetCommand的东西中),当stdin关闭时它不能完全返回。

Python是否认为它是 evil 同时拥有Tkinter GUI并以交互方式使用stdin?

我尝试使用sys.stdin.read(1),但似乎缓冲了一行并返回整行 - 而不是读取一个字节/ char,就像我认为读(1)那样。

1 个答案:

答案 0 :(得分:3)

使线程成为将自动终止的守护程序线程

使用daemon=True启动stdin读取线程。然后它将在主线程终止时自动终止。您不需要明确地对stdin做任何事情。 (您也没有机会在stdin读取线程中进行清理。)例如:

stdinTh = threading.Thread(target=stdinLoop, name="stdinTh")
stdinTh.daemon = True
stdinTh.start()

如果您不能或不想使用守护程序线程

sys.stdin.readline()最终归结为阻塞read()系统调用。

read()关闭时,stdin上的{p> stdin不会返回。我不确定你为什么期望它。这不是特定于Python的行为。至少在我的Linux / glibc系统上,在C中也是如此。

您可以通过向被阻止的线程发送信号(例如read())来突破阻塞SIGUSR1。在C中,您可以使用pthread_kill()。 Python没有提供一种简单的方法来做到这一点,并且有充分的理由;但如果你坚持,你可以do it with ctypes

但更简洁/更安全的方法是使用select.select读取 stdin 一个线程间通信管道,无论哪个先可用:< / p>

import os, select, sys, threading, time

def printer_loop(quit_pipe):
    while True:
        sys.stdout.write("Say something: ")
        sys.stdout.flush()
        (readable, _, _) = select.select([sys.stdin, quit_pipe], [], [])
        if quit_pipe in readable:
            print("Our time is up!")
            break
        # This is not exactly right, because `sys.stdin` could become
        # ready for reading before there's a newline on there, so
        # `readline` could still block. Ideally you would do some
        # custom buffering here.
        line = sys.stdin.readline()
        print("You said: '%s' - well said!" % line.strip())

def main():
    print("Starting thread...")
    (pipe_read, pipe_write) = os.pipe()
    thread = threading.Thread(target=printer_loop, args=(pipe_read,))
    thread.start()
    time.sleep(5)
    print("Interrupting thread...")
    os.write(pipe_write, b'.')
    print("Joining thread...")
    thread.join()
    print("All done!...")

if __name__ == '__main__':
    main()

这不适用于Windows,select()上无法sys.stdin