我的问题很有希望与我读过的任何其他内容无关。我想使用子进程和多处理串行生成一堆作业并将返回代码返回给我。问题是我不想等待()所以我可以一次生成所有作业,但我确实想知道什么时候它完成所以我可以得到返回代码。我有这个奇怪的问题,如果我poll()过程它不会运行。它只是在活动监视器中挂起而没有运行(我在Mac上)。我以为我可以使用一个观察者线程,但我挂在q_out.get()上,这让我相信可能我正在填补缓冲区和死锁。我不知道怎么解决这个问题。这基本上就是我的代码。如果有人对如何做到这一点有任何更好的想法,我会很乐意彻底改变我的方法。
def watchJob(p1,out_q):
while p1.poll() == None:
pass
print "Job is done"
out_q.put(p1.returncode)
def runJob(out_q):
LOGFILE = open('job_to_run.log','w')
p1 = Popen(['../../bin/jobexe','job_to_run'], stdout = LOGFILE)
t = threading.Thread(target=watchJob, args=(p1,out_q))
t.start()
out_q= Queue()
outlst=[]
for i in range(len(nprocs)):
proc = Process(target=runJob, args=(out_q,))
proc.start()
outlst.append(out_q.get()) # This hangs indefinitely
proc.join()
答案 0 :(得分:2)
您既不需要多处理也不需要线程。您可以并行运行多个子进程,并在一个线程中收集它们的法规:
#!/usr/bin/env python3
from subprocess import Popen
def run(cmd, log_filename):
with open(log_filename, 'wb', 0) as logfile:
return Popen(cmd, stdout=logfile)
# start several subprocesses
processes = {run(['echo', c], 'subprocess.%s.log' % c) for c in 'abc'}
# now they all run in parallel
# report as soon as a child process exits
while processes:
for p in processes:
if p.poll() is not None:
processes.remove(p)
print('{} done, status {}'.format(p.args, p.returncode))
break
p.args
将cmd
存储在Python 3.3+中,在早期的Python版本中自行跟踪cmd
。
另见:
要限制并行作业的数量,可以使用ThreadPool(如the first link所示):
#!/usr/bin/env python3
from multiprocessing.dummy import Pool # use threads
from subprocess import Popen
def run_until_done(args):
cmd, log_filename = args
try:
with open(log_filename, 'wb', 0) as logfile:
p = Popen(cmd, stdout=logfile)
return cmd, p.wait(), None
except Exception as e:
return cmd, None, str(e)
commands = ((('echo', str(d)), 'subprocess.%03d.log' % d) for d in range(500))
pool = Pool(128) # 128 concurrent commands at a time
for cmd, status, error in pool.imap_unordered(run_until_done, commands):
if error is None:
fmt = '{cmd} done, status {status}'
else:
fmt = 'failed to run {cmd}, reason: {error}'
print(fmt.format_map(vars())) # or fmt.format(**vars()) on older versions
示例中的线程池有128个线程(不多也不少)。它不能同时执行超过128个作业。只要任何线程释放(完成一个作业),它就会占用另一个,等等。并发执行的作业总数受线程数限制。新工作不会等待所有128个以前的工作完成。它是在任何旧作业完成时启动的。
答案 1 :(得分:1)
如果您要在一个帖子中运行watchJob
,则没有理由忙于p1.poll
;只需调用p1.wait()
即可阻止该流程完成。使用busy循环需要不断释放/重新获取GIL,这会降低主线程的速度,并且还会占用CPU,这会进一步损害性能。
此外,如果您未使用子进程的stdout
,则不应将其发送到PIPE
,因为如果进程写得足够,可能会导致死锁数据到stdout
缓冲区以填充它(实际上可能是你的情况下发生的事情)。这里也没有必要使用multiprocessing
;只需在主线程中调用Popen
,然后让watchJob
线程等待进程完成。
import threading
from subprocess import Popen
from Queue import Queue
def watchJob(p1, out_q):
p1.wait()
out_q.put(p1.returncode)
out_q = Queue()
outlst=[]
p1 = Popen(['../../bin/jobexe','job_to_run'])
t = threading.Thread(target=watchJob, args=(p1,out_q))
t.start()
outlst.append(out_q.get())
t.join()
修改强>
以下是如何以这种方式同时运行多个作业:
out_q = Queue()
outlst = []
threads = []
num_jobs = 3
for _ in range(num_jobs):
p = Popen(['../../bin/jobexe','job_to_run'])
t = threading.Thread(target=watchJob, args=(p1, out_q))
t.start()
# Don't consume from the queue yet.
# All jobs are running, so now we can start
# consuming results from the queue.
for _ in range(num_jobs):
outlst.append(out_q.get())
t.join()