如何区分Multiprocessing.Pool中的进程?

时间:2016-08-24 08:37:53

标签: python linux multiprocessing fork python-multiprocessing

我使用python multiprocessing来分叉一些子进程来运行我的作业。有两个要求:

  1. 如果我想要杀死它,我需要知道子进程的pid。
  2. 工作完成后我需要回调做一些事情。因为这些东西在父进程中使用了锁,所以它不能在子进程中完成。
  3. 但我明白了:

    1. 生成的流程by multiprocessing.Process()具有属性" pid"得到它的pid。但我无法添加异步回调,当然我也无法同步等待。
    2. multiprocessing.Pool()生成的进程池提供回调接口。但是我不能告诉池中的哪个进程是与我的工作匹配的进程,因为我可能需要根据特定的工作终止进程。
    3. 任务很便宜,这里显示代码:

      import random, time
      import multiprocessing
      import os
      
      class Job(object):
          def __init__(self, jobid, jobname, command):
              self.jobid, self.jobname, self.command = jobid, jobname, command
      
          def __str__(self):
              return "Job <{0:05d}>".format(self.jobid)
      
          def __repr__(self):
              return self.__str__()
      
      def _run_job(job):
          time.sleep(1)
          print "{} done".format(job)
          return job, random.choice([True, False]) # the second argument indicates whether job has finished successfully
      
      class Test(object):
          def __init__(self):
              self._loc = multiprocessing.Lock()
              self._process_pool = multiprocessing.Pool()
      
          def submit_job(self, job):
              with self._loc:
                  self._process_pool.apply_async(_run_job, (job,), callback=self.job_done)
                  print "submitting {} successfully".format(job)
      
          def job_done(self, result):
              with self._loc:
                  # stuffs after job has finished is related to some cleanning work, so it needs the lock of the parent process
                  job, success = result
                  if success:
                      print "{} success".format(job)
                  else:
                      print "{} failure".format(job)
      
      
      j1 = Job(1, "test1", "command1")
      j2 = Job(2, "test2", "command2")
      t = Test()
      t.submit_job(j1)
      t.submit_job(j2)
      time.sleep(3.1) # wait for all jobs finishing
      

      但是现在我无法获得与每项工作相对应的pid。例如,我需要终止作业&lt; 1&gt;,但我无法找到流程池中的哪个流程与作业&lt; 1&gt;相关,因此我无法随时杀死该作业。

      如果我选择使用multiprocessing.Process,我可以用相应的jobid记录每个进程的pid。但我现在无法添加回调方法。

      那么有没有办法获得子进程的pid并添加回调方法?

1 个答案:

答案 0 :(得分:0)

最后我找到了一个解决方案:改为使用multiprocessing.Event

由于multiprocessing.Pool无法告诉我哪个进程已分配,因此无法记录,以便我可以随时根据作业ID将其删除。

幸运的是,multiprocessing提供了Event对象作为回调方法的替代方法。回想一下回调方法的作用:它提供了对子进程的异步响应。子进程完成后,父进程可以检测到它并调用回调方法。因此,核心问题是父进程如何检测子进程是否已完成。那是Event的对象。

所以解决方案很简单:将Event对象传递给子进程。子进程完成后,会设置Event对象。在父进程中,它启动一个守护程序线程来监视是否设置了该事件。如果是这样,它可以调用执行那些回调操作的方法。此外,由于我使用multiprocessing.Process而不是multiprocessing.Pool创建了流程,因此我可以轻松获取其PID,这使我能够将其删除。

解决方案代码:

import time
import multiprocessing
import threading

class Job(object):
    def __init__(self, jobid, jobname, command):
        self.jobid, self.jobname, self.command = jobid, jobname, command
        self.lifetime = 0

    def __str__(self):
        return "Job <{0:05d}>".format(self.jobid)

    def __repr__(self):
        return self.__str__()

def _run_job(job, done_event):
    time.sleep(1)
    print "{} done".format(job)
    done_event.set()

class Test(object):
    def __init__(self):
        self._loc = multiprocessing.Lock()
        self._process_pool = {}
        t = threading.Thread(target=self.scan_jobs)
        t.daemon = True
        t.start()

    def scan_jobs(self):
        while True:
            with self._loc:
                done_jobid = []
                for jobid in self._process_pool:
                    process, event = self._process_pool[jobid]
                    if event.is_set():
                        print "Job<{}> is done in process <{}>".format(jobid, process.pid)
                        done_jobid.append(jobid)
                map(self._process_pool.pop, done_jobid)
            time.sleep(1)


    def submit_job(self, job):
        with self._loc:
            done_event = multiprocessing.Event()
            new_process = multiprocessing.Process(target=_run_host_job, args=(job, done_event))
            new_process.daemon = True
            self._process_pool[job.jobid] = (new_process, done_event)
            new_process.start()
            print "submitting {} successfully".format(job)


j1 = Job(1, "test1", "command1")
j2 = Job(2, "test2", "command2")
t = Test()
t.submit_job(j1)
t.submit_job(j2)
time.sleep(5) # wait for job to finish