在线程中,我有一个从用户控制台读取输入的循环。主线程忙于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)那样。
答案 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
。