【问题标题】:Python on Windows - how to wait for multiple child processes?Windows 上的 Python - 如何等待多个子进程?
【发布时间】:2010-09-11 03:59:50
【问题描述】:

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

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

问题是当proc2proc1之前完成时,父进程仍然会等待proc1。在 Unix 上,人们会在循环中使用 waitpid(0) 来获取子进程完成时的返回码 - 如何在 Windows 上的 Python 中实现类似的功能?

【问题讨论】:

  • 你能描述一下你在 unix 上使用的 waitpid(0) 吗?
  • docs.python.org/library/os.html#os.waitpid waitpid(0) 在 unix 上等待(除非 WNOHANG 在选项中)任何可用的子状态并返回 (processid,status) 元组。

标签: python windows asynchronous


【解决方案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)

【讨论】:

    【解决方案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)
    

    【讨论】:

      【解决方案3】:

      基于 zseil 的回答,您可以混合使用子进程和 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!"
      

      【讨论】:

        【解决方案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)
        

        【讨论】:

        • 您对线程解决方案执行主动等待是错误的。我不知道 Twisted。
        【解决方案5】:

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

        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
        

        【讨论】:

        • 好吧,如果调用不支持并行性,那么必须在外部实现它:-) 谢谢!
        • 如果您使用的是 Python 3,则 Queue 模块重命名为 queuethread 模块重命名为 _thread
        • 现在还不是圣诞节,但我希望 Python 添加一个新的 waitForMultiple 函数,该函数可以保护用户免受所有这些低级内容的影响,并且无论我们是否在 Linux 上都能正常工作或窗户。 :)
        【解决方案6】:

        Twisted 有一个asynchronous process-spawning API,可以在 Windows 上运行。实际上有几种不同的实现,其中很多都不是很好,但是您可以在它们之间切换而无需更改代码。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-01-15
          • 1970-01-01
          • 1970-01-01
          • 2014-04-01
          • 2019-08-05
          • 1970-01-01
          相关资源
          最近更新 更多