我使用python multiprocessing
来分叉一些子进程来运行我的作业。有两个要求:
但我明白了:
by multiprocessing.Process()
具有属性" pid"得到它的pid。但我无法添加异步回调,当然我也无法同步等待。 multiprocessing.Pool()
生成的进程池提供回调接口。但是我不能告诉池中的哪个进程是与我的工作匹配的进程,因为我可能需要根据特定的工作终止进程。 任务很便宜,这里显示代码:
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并添加回调方法?
答案 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