Python子进程在主进程从stdin进行阻塞读取时被阻塞

3 投票
1 回答
1729 浏览
提问于 2025-04-19 23:48

我有一个用Python写的多进程应用程序,它通过多进程API启动“工作者”。这个主进程是由一个不是用Python写的服务进程启动的。工作者可能会使用subprocess.Popen来启动其他非Python的子进程。

为了更清楚,这里是整个进程的层级结构:

  • service.exe: 服务进程(本地可执行文件)
    • python.exe: Python主进程(下面的程序)
      • python.exe: Python子进程(由Process启动的任务函数)
        • subprocess.exe: 本地子进程(见下面的解释)

当服务进程停止时,它必须告诉Python进程退出。我是通过标准输入来实现这个的。这样做的好处是,如果服务进程崩溃或被杀掉,Python进程的标准输入就会关闭,这样它会退出,就不会留下孤儿进程。

import multiprocessing
import time
import sys


def task():
    print("Task started...")
    # TODO: Start a native process here using Subprocess.popen
    time.sleep(3)
    print("Task ended")

if __name__ == '__main__':
    process = multiprocessing.Process(target=task)
    process.start()

    # time.sleep(3)  # "workaround"
    sys.stdin.read()

    print("Terminating process...")
    process.terminate()

但是,似乎当我添加sys.stdin.read()时,Python子进程虽然启动了,但什么都不做。它看起来就像是卡住了一样。

一个(不好的)解决办法是,在读取标准输入之前加上time.sleep(3)。这样,上面的程序就能正常工作了。不过,似乎由Python子进程启动的子进程仍然可能会被阻塞,而且只有在我在主进程中进行阻塞读取时,它们才会被阻塞。

这个问题并不是在所有系统上都会出现。我在一台Windows 8的机器上观察到了这个问题,而在另一台Windows机器上则从未发生过。我使用的是Python 2.7.2。

我的问题是:主进程中的阻塞读取怎么会影响子进程?难道子进程不应该独立于主进程的操作而启动和运行吗?(我只是想理解这个问题。如果你能找到一个更好的方法来让服务进程停止Python进程,我会很感激,但这个奇怪的阻塞行为让我很困扰。)

1 个回答

0

你的子进程并没有卡住。使用多进程库时,我最喜欢的一种调试方法是让子进程把信息写入文本文件,而不是直接打印到屏幕上。这样可以避免很多麻烦,比如不确定子进程是否继承了相同的输入输出,管道是否满了等等。如果我们把你的任务改成下面这样:

def task():
    with open('taskfile.txt', 'w') as fo:
        fo.write("Task started...")
        # TODO: Start a native process here using Subprocess.popen
        time.sleep(3)
        fo.write("Task ended")

它会生成一个名为'taskfile.txt'的文本文件,里面包含以下内容:

任务开始...任务结束

所以你的任务运行和结束得很好。主程序只是等待输入。 我猜你没有看到“任务开始...”的提示,是因为用multiprocessing.Process()启动的进程有自己的输入输出管道,这些管道并没有连接到主程序的控制台上。

撰写回答