我正在开发一个renderfarm,我需要我的客户端能够启动多个渲染器实例,而不会阻塞,因此客户端可以接收新命令。我已经正常工作,但是我无法终止创建的进程。
在全局层面,我定义了我的池(以便我可以从任何函数访问它):
p = Pool(2)
然后我用apply_async调用我的渲染器:
for i in range(totalInstances):
p.apply_async(render, (allRenderArgs[i],args[2]), callback=renderFinished)
p.close()
该函数完成,在后台启动进程,并等待新命令。我做了一个简单的命令,会杀死客户端并停止渲染:
def close():
'close this client instance'
tn.write ("say "+USER+" is leaving the farm\r\n")
try:
p.terminate()
except Exception,e:
print str(e)
sys.exit()
sys.exit()
它似乎没有给出错误(它会打印错误),python终止但后台进程仍在运行。任何人都可以推荐一种更好的方法来控制这些推出的程序吗?
答案 0 :(得分:7)
我找到了解决方案:在单独的线程中停止池,如下所示:
def close_pool():
global pool
pool.close()
pool.terminate()
pool.join()
def term(*args,**kwargs):
sys.stderr.write('\nStopping...')
# httpd.shutdown()
stophttp = threading.Thread(target=httpd.shutdown)
stophttp.start()
stoppool=threading.Thread(target=close_pool)
stoppool.daemon=True
stoppool.start()
signal.signal(signal.SIGTERM, term)
signal.signal(signal.SIGINT, term)
signal.signal(signal.SIGQUIT, term)
工作正常,我经常测试。
答案 1 :(得分:5)
如果您仍然遇到此问题,可以尝试使用daemonic processes模拟Pool
(假设您正在从非守护进程启动池/进程)。我怀疑这是最好的解决方案,因为好像你的Pool
进程应该退出,但这是我能想到的。我不知道你的回调是做什么的,所以我不确定在下面的例子中把它放在哪里。
我还建议尝试在Pool
中根据我的经验(和文档)创建__main__
,并在全局生成流程时出现奇怪现象。如果您使用Windows,则尤其如此:http://docs.python.org/2/library/multiprocessing.html#windows
from multiprocessing import Process, JoinableQueue
# the function for each process in our pool
def pool_func(q):
while True:
allRenderArg, otherArg = q.get() # blocks until the queue has an item
try:
render(allRenderArg, otherArg)
finally: q.task_done()
# best practice to go through main for multiprocessing
if __name__=='__main__':
# create the pool
pool_size = 2
pool = []
q = JoinableQueue()
for x in range(pool_size):
pool.append(Process(target=pool_func, args=(q,)))
# start the pool, making it "daemonic" (the pool should exit when this proc exits)
for p in pool:
p.daemon = True
p.start()
# submit jobs to the queue
for i in range(totalInstances):
q.put((allRenderArgs[i], args[2]))
# wait for all tasks to complete, then exit
q.join()
答案 2 :(得分:0)
# -*- coding:utf-8 -*-
import multiprocessing
import time
import sys
import threading
from functools import partial
#> work func
def f(a,b,c,d,e):
print('start')
time.sleep(4)
print(a,b,c,d,e)
###########> subProcess func
#1. start a thead for work func
#2. waiting thead with a timeout
#3. exit the subProcess
###########
def mulPro(f, *args, **kwargs):
timeout = kwargs.get('timeout',None)
#1.
t = threading.Thread(target=f, args=args)
t.setDaemon(True)
t.start()
#2.
t.join(timeout)
#3.
sys.exit()
if __name__ == "__main__":
p = multiprocessing.Pool(5)
for i in range(5):
#1. process the work func with "subProcess func"
new_f = partial(mulPro, f, timeout=8)
#2. fire on
p.apply_async(new_f, args=(1,2,3,4,5),)
# p.apply_async(f, args=(1,2,3,4,5), timeout=2)
for i in range(10):
time.sleep(1)
print(i+1,"s")
p.close()
# p.join()
答案 3 :(得分:-5)
找到我自己的问题的答案。主要问题是我在调用第三方应用程序而不是函数。当我调用子进程[使用call()或Popen()]时,它会创建一个新的python实例,其唯一目的是调用新的应用程序。但是当python退出时,它将终止这个新的python实例并让应用程序继续运行。
解决方案是通过查找创建的python进程的pid,获取该pid的子进程并杀死它们来艰难地进行。此代码特定于osx;有更简单的代码(不依赖于grep)可用于linux。
for process in pool:
processId = process.pid
print "attempting to terminate "+str(processId)
command = " ps -o pid,ppid -ax | grep "+str(processId)+" | cut -f 1 -d \" \" | tail -1"
ps_command = Popen(command, shell=True, stdout=PIPE)
ps_output = ps_command.stdout.read()
retcode = ps_command.wait()
assert retcode == 0, "ps command returned %d" % retcode
print "child process pid: "+ str(ps_output)
os.kill(int(ps_output), signal.SIGTERM)
os.kill(int(processId), signal.SIGTERM)