【问题标题】:How do you terminate/break/abort a Python console/sys.stdin readline()?如何终止/中断/中止 Python 控制台/sys.stdin readline()?
【发布时间】:2017-08-07 21:47:41
【问题描述】:

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

主线程调用 startGUI(),其中包含一个 tk.mainloop() 调用。当我按下窗口(这是 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")

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

我认为 Python readline() 正在做一些 聪明的(在称为 NetCommand 的东西中),当 stdin 关闭时不会干净地返回。

Python 是否认为同时拥有 Tkinter GUI 和交互使用标准输入是邪恶?

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

【问题讨论】:

  • Python 并不认为它是邪恶的,但它肯定不是 GUI 程序的设计运行方式。根据您启动 GUI 的方式,甚至可能没有可供读取的标准输入。您想通过 GUI 和从标准输入读取来完成什么?
  • @BryanOakley,是的,不是传统的,我使用控制台向应用程序输入“调试”命令。 GUI 是(最终)用户将与之交互的真实事物。

标签: python multithreading python-2.7 tkinter


【解决方案1】:

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

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

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

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

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

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

您可以通过向阻塞线程发送信号(例如SIGUSR1)来突破阻塞read()。在 C 语言中,您可以为此使用 pthread_kill()。 Python 没有提供一种简单的方法来做到这一点,这是有充分理由的;但如果你坚持,你可以do it with 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()

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

【讨论】:

  • 我认为 read() 会在管道关闭时终止,因为它会看到 EOF,对吧? (我会尝试 deamon=true 和 select.select。)奇怪的是 read(1) 返回一个完整的行,其中 read(1) 在“文件”对象上(stdin 应该是文件对象) t it?) 应该返回 1 个字节/字符。
  • @Ribo “我认为 read() 会在管道关闭时终止,因为它会看到 EOF”——正如我所说,即使在 C 中也不会发生这种情况
  • @Ribo “read(1) 返回一个完整的行”——我不知道为什么;无法复制;可能最好问一个单独的问题
  • @Ribo 和 Faronov。如 cmets 中所述,线程在 readline 处阻塞。为什么标记为正确?好像不太对。
  • @DrM 在 Ribo 描述的情况下,它不应该阻塞,至少通常不会,因为控制台不会向您的标准输入发送任何内容,直到它有整行,即直到用户点击 Enter .也许你的情况不同?最好单独提出一个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-02
  • 1970-01-01
  • 1970-01-01
  • 2017-04-22
相关资源
最近更新 更多