subprocess.wait() 未等待 Popen 进程完成(使用线程时)?

24 投票
4 回答
60159 浏览
提问于 2025-04-16 19:31

我在用 subprocess.Popen() 从我的 Python 脚本中同时启动多个相同应用程序的实例时遇到了一些问题。我是通过线程来实现的,每个线程里我用 popen() 来运行应用程序,然后再用 wait() 等待它完成。问题是,wait() 似乎并没有真正等待进程结束。我尝试只用一个线程,并在进程开始和结束时打印一些文本消息。这样线程的函数大概是这样的:

def worker():
    while True:
        job = q.get() # q is a global Queue of jobs
        print('Starting process %d' % job['id'])
        proc = subprocess.Popen(job['cmd'], shell=True)
        proc.wait()
        print('Finished process %d' % job['id'])
        job.task_done()

但是即使我只用一个线程,它也会在任何 "Finished process..." 消息出现之前,打印出好几条 "Starting process..." 的消息。有没有可能在某些情况下 wait() 不会真正等待呢?我有几个不同的外部应用程序(C++ 控制台应用),它们会同时运行多个实例,对其中一些应用,我的代码能正常工作,但对其他的就不行。是不是外部应用程序本身有问题,导致 wait() 的调用受到影响呢?创建线程的代码大概是这样的:

for i in range(1):
    t = Thread(target=worker)
    t.daemon = True
    t.start()
q.join() # Wait for the queue to empty

更新 1: 我还要补充一点,对于某些外部应用程序,我有时会得到一个返回码(proc.returncode)为 -1073471801。例如,其中一个外部应用在调用 Popen 的前两次会返回这个码,但在后两次(当我有四个任务时)就不会了。

更新 2: 为了澄清一下,现在我有四个任务在队列中,都是四个不同的测试用例。当我运行我的代码时,对于一个外部应用,前两次 Popen 调用会生成返回码 -1073471801。但是如果我打印出 Popen 调用的确切命令,并在命令窗口中运行它,它就能正常执行。

解决了! 我终于解决了我遇到的问题。我觉得问题出在我对线程编程的经验不足。我没有意识到,当我创建了第一个工作线程后,它们会一直存在,直到 Python 脚本退出。每次我在队列中放新项目时,我错误地创建了更多的工作线程(我对每个想要运行的外部程序都是批量处理)。所以当我到达第四个外部应用时,实际上有四个线程同时在运行,尽管我只认为我有一个。

4 个回答

0

确保你调用的所有应用程序在结束时都有有效的系统返回代码。

13

很遗憾,当你使用 shell=True 来运行子进程时,wait( 只会等待 sh 这个子进程结束,而不会等你实际运行的命令 cmd 完成。

我建议如果可以的话,尽量不要使用 shell=True。如果实在不能,你可以像这个 回答 中提到的那样,创建一个进程组,然后使用 os.waitpid 来等待整个进程组,而不仅仅是等待 shell 进程。

希望这些信息对你有帮助 :)

14

你也可以用 check_call() 来代替 Popen。check_call() 会等命令执行完毕,即使你设置了 shell=True,然后它会返回这个任务的退出代码。

撰写回答