Python 子进程 readline() 挂起;不能使用普通选项

标签 python multithreading subprocess

首先,我知道这看起来像是重复的。我一直在阅读:

Python subprocess readlines() hangs

Python Subprocess readline hangs() after reading all input

subprocess readline hangs waiting for EOF

但是这些选项要么直接不起作用,要么我不能使用它们。

问题

# Obviously, swap HOSTNAME1 and HOSTNAME2 with something real
cmd = "ssh -N -f -L 1111:<HOSTNAME1>:80 <HOSTNAME2>"

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=os.environ)
while True:
    out = p.stdout.readline()
    # Hangs here ^^^^^^^ forever

    out = out.decode('utf-8')
    if out:
        print(out)
    if p.poll() is not None:
        break

我的困境是调用 subprocess.Popen() 的函数是一个运行bash命令的库函数,所以需要非常通用,并且有以下限制:
  • 必须在输入时显示输出;不要阻止然后一次性向屏幕发送垃圾邮件
  • 如果父调用者正在对库函数进行多处理,则不能使用多处理(Python 不允许子进程拥有子进程)
  • 无法使用 signal.SIGALRM出于与多处理相同的原因;父调用者可能试图设置自己的超时
  • 无法使用第三方非内置模块
  • 直接线程是不行的。当readline()调用在线程中,thread.join(timeout=1)让程序继续,但是 ctrl+c 根本不起作用,并调用 sys.exit()不退出程序,因为线程仍然打开。如您所知,您不能通过设计杀死python中的线程。
  • 没有任何方式的 bufsize 或其他子进程参数似乎有所作为;也不会将 readline() 放入迭代器中。

  • 如果可以的话,我会有一个可行的解决方案杀死一个线程 ,但这是 super 禁忌,尽管这绝对是一个合法的用例。

    我对任何想法持开放态度。

    最佳答案

    一种选择是使用线程发布到队列。然后你可以在队列中阻塞超时。您可以使阅读器线程成为守护程序,这样它就不会阻止系统退出。这是一个草图:

    import subprocess
    from threading import Thread
    from queue import Queue
    
    def reader(stream, queue):
        while True:
            line = stream.readline()
            queue.put(line)
            if not line:
                break
    
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, ...)
    queue = Queue()
    thread = Thread(target=reader, args=(p.stdout, queue))
    thread.daemon = True
    thread.start()
    while True:
        out = queue.get(timeout=1)  # timeout is optional
        if not out:  # Reached end of stream
            break
        ...  # Do whatever with output
    
    # Output stream was closed but process may still be running
    p.wait()
    

    请注意,您应该根据您的特定用例调整此答案。例如,您可能希望添加一种方法来向读取器线程发出信号以在到达流的末尾之前停止运行。

    另一种选择是轮询输入流,就像在这个问题中一样:timeout on subprocess readline in python

    关于Python 子进程 readline() 挂起;不能使用普通选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58471094/

    相关文章:

    python - django-rest-framework:无法调用 `.is_valid()`,因为在实例化序列化程序实例时没有传递 `data=` 关键字参数

    java - Fork Join 池挂起

    java - 需要设计方案 : Enabling menu item from outside JFrame

    python - 如何生成进程并忽略所有用户提示?

    python - 使用 PyDict_SetItemString 进行引用计数

    python - 训练损失不减少

    python - 通过 HTTPS 运行 Eve

    c++ - NUMA:如何检查分配了 C++ 数组的 RAM 的哪一部分?

    python - 在 cherrypy 中成功快速启动后启动后台进程

    python - 捕获 python 子进程的输出