如何终止/中断/中止Python控制台/sys.stdin readline()?

Rib*_*ibo 3 python multithreading tkinter python-2.7

在线程中,我有一个循环从用户控制台读取输入。主线程忙于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
Run Code Online (Sandbox Code Playgroud)

主主题调用包含tk.mainloop()调用的startGUI()。当我按下窗口(这是Linux)上的X关闭按钮时,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")
Run Code Online (Sandbox Code Playgroud)

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

我认为Python readline()做的聪明的事情(在NetCommand中)在关闭stdin时不会干净地返回。

Python认为同时拥有Tkinter GUI和交互使用stdin 是否有害

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

Vas*_*nov 6

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

使用来启动stdin-reading线程daemon=True。当主线程终止时,它将自动终止。您不需要显式地使用stdin进行任何操作。(您也没有机会在stdin读取线程中进行清理。)例如:

stdinTh = threading.Thread(target=stdinLoop, name="stdinTh")
stdinTh.daemon = True
stdinTh.start()
Run Code Online (Sandbox Code Playgroud)

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

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

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

您可以read()通过向SIGUSR1被阻止的线程发送信号(例如)来突破阻塞。在C语言中,您可以使用pthread_kill()它。Python并没有提供简便的方法来执行此操作,这是有充分理由的。但是如果您坚持使用,可以使用ctypes

但一个更清洁/更安全的方法是使用select.select从读取或者标准输入线程间通信管道,取其可用第一:

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()
Run Code Online (Sandbox Code Playgroud)

这不能移植到Windows上,而您不能select()在Windows上移植sys.stdin