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

Posted

技术标签:

【中文标题】subprocess.wait() 不等待 Popen 进程完成(使用线程时)?【英文标题】:subprocess.wait() not waiting for Popen process to finish (when using threads)? 【发布时间】:2011-09-14 13:02:14 【问题描述】:

我在使用subprocess.Popen() 从我的 python 脚本中使用线程生成同一应用程序的多个实例以使它们同时运行时遇到了一些问题。在每个线程中,我使用popen() 调用运行应用程序,然后通过调用wait() 等待它完成。问题似乎是wait()-call 实际上并没有等待进程完成。我尝试只使用一个线程,并在进程开始和结束时打印出文本消息。所以线程函数看起来像这样:

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()

但即使我只使用一个线程,它也会在出现任何“已完成的过程...”消息之前打印出几条“正在启动过程...”消息。有没有 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: 我还应该补充一点,对于某些外部应用程序,我有时会收到 -1073471801 的返回码 (proc.returncode)。例如,其中一个外部应用程序将在前两次调用 Popen 时给出该返回码,但不是最后两次(当我有四个作业时)。

更新 2: 澄清一下,现在我在队列中有四个作业,它们是四个不同的测试用例。当我运行我的代码时,对于其中一个外部应用程序,前两个 Popen-调用会生成返回码 -1073471801。但是,如果我打印出Popen 调用的确切命令,并在命令窗口中运行它,它就会毫无问题地执行。

解决了! 我设法解决了我遇到的问题。我认为问题在于我缺乏线程编程方面的经验。我错过了这样一个事实,即当我创建我的第一个工作线程时,它们会继续存在直到 python 脚本退出。每次我将新项目放入队列时,我都会错误地创建更多工作线程(我为每个要运行的外部程序分批执行此操作)。所以当我到达第四个外部应用程序时,我有四个线程同时运行,尽管我只以为我有一个。

【问题讨论】:

尝试print proc.wait() 并检查返回码。 【参考方案1】:

您也可以使用check_call() 代替 Popen。 check_call() 等待命令完成,即使 shell=True 然后返回作业的退出代码。

【讨论】:

好像Popen也不等stdin=ps.stdout的时候。【参考方案2】:

遗憾的是,当使用 shell=True 运行子进程时,wait() 只会等待 sh 子进程完成,而不是等待命令 cmd

我会建议是否可以不使用shell=True,如果不可能,您可以创建一个类似answer 的进程组并使用os.waitpid 来等待进程组而不仅仅是shell 进程.

希望这会有所帮助:)

【讨论】:

就我自己而言,我从未发现shell=True 是必要或有用的情况。如果您从其他地方获取它,它可以让您变得懒惰并且不会拆分字符串,但是不需要引用参数(在不同平台上进行不同的解析)是非常宝贵的 - 我更喜欢传递一个可迭代的而不是而不是带有引号值的字符串,在拆分时您必须小心。 @Chris:甚至文档建议不要使用shell=True,特别是当要执行的数据来自不受信任的来源以避免shell注入时。 在某些情况下,如果不使用shell= True,您将获得OSError: [Errno 2] No such file or directory【参考方案3】:

确保您调用的所有应用程序在完成时都有有效的系统返回码

【讨论】:

【参考方案4】:

我也遇到了问题,但受到了你的启发。

我的看起来像这样,而且效果很好:

    startupinfo = subprocess.STARTUPINFO()
    startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
    startupinfo.wShowWindow = subprocess.SW_HIDE
    proc = subprocess.Popen(command, startupinfo=startupinfo)
    proc.communicate()
    proc.wait()

注意这个也隐藏了窗口。

【讨论】:

以上是关于subprocess.wait() 不等待 Popen 进程完成(使用线程时)?的主要内容,如果未能解决你的问题,请参考以下文章

在线程争用下等待的最快方法

Python——POP3邮件协议

栈是常用的一种数据结构,有 n 个元素在栈顶端一侧等待进栈,栈顶端另一侧是 出栈序列。你已经知道栈的操作有两种:push 和 pop,前者是将一个元素进栈,后 者是将栈顶元素弹出。现在要使用这两种操作

redis push/pop(List)的17条命令

如何实现一个等待元素弹出的堆栈

PHP array_pop 不工作