24

如何在 Windows 上等待 Python 中的多个子进程,而无需主动等待(轮询)?像这样的东西几乎对我有用:

proc1 = subprocess.Popen(['python','mytest.py'])
proc2 = subprocess.Popen(['python','mytest.py'])    
proc1.wait()
print "1 finished"
proc2.wait()
print "2 finished"

问题是当proc2完成之前proc1,父进程仍然会等待proc1。在 Unix 上,人们会waitpid(0)在一个循环中使用它们来获取子进程完成时的返回码——如何在 Windows 上的 Python 中实现这样的事情?

4

6 回答 6

18

这似乎有点矫枉过正,但是,这里是这样的:

import Queue, thread, subprocess

results= Queue.Queue()
def process_waiter(popen, description, que):
    try: popen.wait()
    finally: que.put( (description, popen.returncode) )
process_count= 0

proc1= subprocess.Popen( ['python', 'mytest.py'] )
thread.start_new_thread(process_waiter,
    (proc1, "1 finished", results))
process_count+= 1

proc2= subprocess.Popen( ['python', 'mytest.py'] )
thread.start_new_thread(process_waiter,
    (proc2, "2 finished", results))
process_count+= 1

# etc

while process_count > 0:
    description, rc= results.get()
    print "job", description, "ended with rc =", rc
    process_count-= 1
于 2008-09-19T10:09:47.050 回答
5

Twisted 有一个在 Windows 上工作的异步进程生成 API 。实际上有几种不同的实现,其中很多都不是很好,但是您可以在它们之间切换而无需更改代码。

于 2008-09-21T15:25:33.770 回答
5

基于 zseil 的回答,您可以混合使用 subprocess 和 win32 API 调用来完成此操作。我使用了直接的 ctypes,因为我的 Python 没有安装 win32api。我只是在这里从 MSYS 生成 sleep.exe 作为示例,但显然你可以生成任何你喜欢的进程。我使用 OpenProcess() 从进程的 PID 中获取 HANDLE,然后使用 WaitForMultipleObjects 等待任何进程完成。

import ctypes, subprocess
from random import randint
SYNCHRONIZE=0x00100000
INFINITE = -1
numprocs = 5
handles = {}

for i in xrange(numprocs):
    sleeptime = randint(5,10)
    p = subprocess.Popen([r"c:\msys\1.0\bin\sleep.exe", str(sleeptime)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
    h = ctypes.windll.kernel32.OpenProcess(SYNCHRONIZE, False, p.pid)
    handles[h] = p.pid
    print "Spawned Process %d" % p.pid

while len(handles) > 0:
    print "Waiting for %d children..." % len(handles)
    arrtype = ctypes.c_long * len(handles)
    handle_array = arrtype(*handles.keys())
    ret = ctypes.windll.kernel32.WaitForMultipleObjects(len(handle_array), handle_array, False, INFINITE)
    h = handle_array[ret]
    ctypes.windll.kernel32.CloseHandle(h)
    print "Process %d done" % handles[h]
    del handles[h]
print "All done!"
于 2009-02-21T15:12:19.607 回答
4

Windows 上的 Twisted 将在幕后执行主动等待。如果您不想使用线程,则必须使用 win32 API 来避免轮询。像这样的东西:

import win32process
import win32event

# Note: CreateProcess() args are somewhat cryptic, look them up on MSDN
proc1, thread1, pid1, tid1 = win32process.CreateProcess(...)
proc2, thread2, pid2, tid2 = win32process.CreateProcess(...)
thread1.close()
thread2.close()

processes = {proc1: "proc1", proc2: "proc2"}

while processes:
    handles = processes.keys()
    # Note: WaitForMultipleObjects() supports at most 64 processes at a time
    index = win32event.WaitForMultipleObjects(handles, False, win32event.INFINITE)
    finished = handles[index]
    exitcode = win32process.GetExitCodeProcess(finished)
    procname = processes.pop(finished)
    finished.close()
    print "Subprocess %s finished with exit code %d" % (procname, exitcode)
于 2008-09-29T15:52:35.563 回答
2

您可以使用psutil

>>> import subprocess
>>> import psutil
>>> 
>>> proc1 = subprocess.Popen(['python','mytest.py'])
>>> proc2 = subprocess.Popen(['python','mytest.py'])    
>>> ls = [psutil.Process(proc1.pid), psutil.Process(proc2.pid)]
>>>
>>> gone, alive = psutil.wait_procs(ls, timeout=3)

'gone' 和 'alive' 是指示哪些进程已经消失以及哪些进程仍然存在的列表。

或者,您可以指定一个回调,该回调在每次被监视进程之一终止时被调用:

>>> def on_terminate(proc):
...     print "%s terminated" % proc
...
>>> gone, alive = psutil.wait_procs(ls, timeout=3, callback=on_terminate)
于 2013-11-29T19:17:31.900 回答
1

你可以使用psutil

import psutil

with psutil.Popen(["python", "mytest.py"]) as proc1, psutil.Popen(
    ["python", "mytest.py"]
) as proc2:
    gone, alive = psutil.wait_procs([proc1, proc2], timeout=3)

'gone' 和 'alive' 是指示哪些进程已经消失以及哪些进程仍然存在的列表。

或者,您可以指定一个回调,该回调在每次被监视进程之一终止时被调用:

def on_terminate(proc):
    print "%s terminated" % proc

gone, alive = psutil.wait_procs(ls, timeout=3, callback=on_terminate)
于 2020-11-14T09:57:59.927 回答