Python中多处理池中的作业动态重新排序

Python中多处理池中的作业动态重新排序,python,python-2.7,multiprocessing,pool,Python,Python 2.7,Multiprocessing,Pool,我正在编写一个python脚本(用于cygwin和linux环境),以便在使用subprocess.Popen()从命令行运行的程序上运行回归测试。基本上,我有一组作业,其中的一个子集需要根据开发人员的需要运行(大约10到1000)。完成每项工作可能需要几秒钟到20分钟 我的作业成功地跨多个处理器运行,但我正试图通过智能地排序作业(基于过去的性能)以首先运行较长的作业来节省一些时间。复杂的是,一些作业(稳态计算)需要在其他作业(基于稳态确定的初始条件的瞬态)之前运行 我目前的处理方法是在同一进程

我正在编写一个python脚本(用于cygwin和linux环境),以便在使用subprocess.Popen()从命令行运行的程序上运行回归测试。基本上,我有一组作业,其中的一个子集需要根据开发人员的需要运行(大约10到1000)。完成每项工作可能需要几秒钟到20分钟

我的作业成功地跨多个处理器运行,但我正试图通过智能地排序作业(基于过去的性能)以首先运行较长的作业来节省一些时间。复杂的是,一些作业(稳态计算)需要在其他作业(基于稳态确定的初始条件的瞬态)之前运行

我目前的处理方法是在同一进程上递归运行父作业和所有子作业,但有些作业有多个长期运行的子作业。父作业完成后,我希望将子作业添加回池中,以将其转出到其他进程,但需要将它们添加到队列的头部。我不确定我是否可以用multiprocessing.Pool来实现这一点。我寻找了Manager的例子,但它们似乎都是基于网络的,并不特别适用。如果您能以代码或链接的形式提供关于多处理的好教程的帮助(我在谷歌上搜索过…),我们将不胜感激。这里是我到目前为止得到的代码的框架,注释指出了我希望在其他处理器上产生的子作业

import multiprocessing
import subprocess

class Job(object):
  def __init__(self, popenArgs, runTime, children)
    self.popenArgs = popenArgs #list to be fed to popen
    self.runTime = runTime #Approximate runTime for the job
    self.children = children #Jobs that require this job to run first

def runJob(job):
  subprocess.Popen(job.popenArgs).wait()
  ####################################################
  #I want to remove this, and instead kick these back to the pool
  for j in job.children: 
    runJob(j)
  ####################################################

def main(jobs):
  # This jobs argument contains only jobs which are ready to be run
  # ie no children, only parent-less jobs
  jobs.sort(key=lambda job: job.runTime, reverse=True)
  multiprocessing.Pool(4).map(runJob, jobs)

首先,让我重复Armin Rigo的评论:这里没有理由使用多进程而不是多线程。在控制过程中,您花费了大部分时间等待子过程完成;您没有需要并行化的CPU密集型工作

使用线程还可以更容易地解决主要问题。现在,您正在将作业存储在其他作业的属性(隐式依赖关系图)中。您需要一个单独的数据结构,根据调度对作业进行排序。此外,每个作业树当前都绑定到一个辅助进程。您希望将工作人员与用于保存作业的数据结构分离。然后,每个工人从相同的任务队列中提取作业;工作人员完成其工作后,会将工作的子项排队,然后可以由任何可用的工作人员处理这些子项

由于您希望在父作业完成时将子作业插入到行的前端,因此堆栈式容器似乎适合您的需要;
Queue
模块提供了一个线程安全的
LifoQueue
类,您可以使用它

import threading
import subprocess
from Queue import LifoQueue

class Job(object):
  def __init__(self, popenArgs, runTime, children):
    self.popenArgs = popenArgs
    self.runTime = runTime
    self.children = children

def run_jobs(queue):
  while True:
    job = queue.get()
    subprocess.Popen(job.popenArgs).wait()
    for child in job.children: 
      queue.put(child)
    queue.task_done()

# Parameter 'jobs' contains the jobs that have no parent.
def main(jobs):
  job_queue = LifoQueue()
  num_workers = 4
  jobs.sort(key=lambda job: job.runTime)
  for job in jobs:
    job_queue.put(job)
  for i in range(num_workers):
    t = threading.Thread(target=run_jobs, args=(job_queue,))
    t.daemon = True
    t.start()
  job_queue.join()
注意:(1)我们无法通过监视工作线程来知道何时完成了所有工作,因为它们不跟踪要完成的工作。这是队列的工作。因此,主线程监视队列对象以了解所有工作何时完成(
job\u queue.join()
)。因此,我们可以将工作线程标记为守护进程线程,这样只要主线程执行,进程就会退出,而不必等待工作线程。因此,我们不需要在主线程和工作线程之间进行通信,以便告诉后者何时中断循环并停止


(2) 我们知道,当所有已排队的任务都标记为已完成时(特别是当
task\u done()
被调用的次数等于已排队的项目数时),所有工作都已完成。将队列为空作为所有工作都已完成的条件是不可靠的;在从队列中弹出一个作业和将该作业的子作业排入队列之间,队列可能会短暂且误导性地空着。

随机提示:我建议不要在这里使用多处理,因为它不是用于此目的的。您可以使用线程获得相同的结果,甚至使用传统的方法,即启动并等待进程(
子进程
模块和
os.wait()
),我认为这已经做到了。让我完成测试,我会给你支票的。非常感谢你的帮助!