2013-05-06 169 views
8

我正在使用renderfarm,我需要我的客户端能够启动渲染器的多个实例,而不会阻塞,因此客户端可以接收新命令。我的工作正常,但是我无法终止创建的流程。python多处理池终止

在全球层面上,我定义我的游泳池(这样我可以从任何函数访问):

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() 

它似乎并没有给出错误(这将打印错误),蟒蛇终止但后台进程仍运行。任何人都可以推荐一种更好的方式来控制这些启动的程

+0

尝试启用调试日志记录'from multiprocessing import util; util.get_logger().setLevel(util.DEBUG)'并粘贴输出。 – schlamar 2013-05-06 14:48:03

+2

我以前见过类似这样的行为,但现在无法重现它......我不知道在调用p.terminate()之后调用p.join()会有所帮助吗?我还想知道,如果你甚至需要调用terminate,并且只是在执行sys.exit()会正确地垃圾收集池及其所有进程。 – mdscruggs 2013-05-06 15:21:15

+0

当我尝试启用日志记录时,我在控制台中得到了这个:“没有处理程序可以找到logger”multiprocessing“。不幸的是,p.terminate()之后的p.join()没有什么区别,sys .exit()关闭python,但让进程在后台运行。 – tk421storm 2013-05-07 15:37:16

回答

-4

找到了我自己问题的答案。主要问题是我正在调用第三方应用程序而不是函数。当我使用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) 
5

如果您仍然遇到此问题,可以尝试使用daemonic processes模拟Pool(假设您从非守护进程启动池/进程)。我怀疑这是最好的解决方案,因为它看起来像你的Pool进程应该退出,但这是我能想到的。我不知道你的回调是干什么的,所以我不确定把它放在下面的例子中。

我还建议您尝试在__main__中创建您的Pool,因为我的经验(和文档)在世界各地产生进程时会发生奇怪现象。如果你在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() 
+1

有趣!关于在主要而不是全局中定义的好建议。我重建了这种方式,并没有解决我的问题(见下文),但我更喜欢这种结构。谢谢! – tk421storm 2013-05-14 14:15:42

5

我找到了解决办法:停止池单独的线程,就像这样:

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) 

工作正常,我总是测试。