使用subprocess.Popen()从我的python脚本使用线程生成同一应用程序的多个实例以使其同时运行时,遇到了一些问题。在每个线程中,我使用popen()调用运行应用程序,然后通过调用wait()等待它完成。问题似乎是wait()调用实际上没有等待进程完成。我仅使用一个线程进行了实验,并在进程开始和结束时打印出了文本消息。所以线程函数看起来像这样:
1
2
3
4
5
6
7
8def 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()的调用?
用于创建线程的代码如下所示:
1
2
3
4
5for 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()并检查返回码。
您也可以使用check_call()代替Popen。 即使shell=True,check_call()也要等待命令完成,然后返回作业的退出代码。
使用shell = True似乎实际上并没有等待。
似乎时Popen也不会等待。
可悲的是,当使用shell=True运行子流程时,wait()将仅等待sh子流程完成,而不会等待命令cmd。
我将建议是否可以不使用shell=True,如果不可能的话,您可以创建一个类似于此答案的进程组,并使用os.waitpid来等待进程组,而不仅仅是shell进程。
希望这对您有所帮助:)
对于我自己,我从未发现shell=True是必要或有用的情况。 如果您从其他地方获取字符串,它可以让您变得懒惰而不会将字符串分割开,但是缺少引号的需要(在不同平台上具有不同的解析)是非常宝贵的-我更喜欢传递可迭代的字符串而不是使用 引用值,在拆分时必须要小心。
@Chris:甚至是文档建议也不要使用shell=True,特别是当要执行的数据来自不受信任的来源时,以避免注入外壳。
在某些情况下,如果不使用shell= True,将会得到OSError: [Errno 2] No such file or directory
我也遇到了问题,但是受到了你的启发。
我的看起来像这样,并且做工精美:
1
2
3
4
5
6startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
proc = subprocess.Popen(command, startupinfo=startupinfo)
proc.communicate()
proc.wait()
请注意,这也隐藏了窗口。
确保您正在调用的所有应用程序在完成时都具有有效的系统返回码