【问题标题】:how to to terminate process using python's multiprocessing如何使用python的多处理终止进程
【发布时间】:2015-11-10 06:44:20
【问题描述】:

我有一些代码需要在其他几个可能挂起或出现不受我控制的问题的系统上运行。我想使用 python 的多处理来生成子进程以独立于主程序运行,然后当它们挂起或出现问题时终止它们,但我不确定解决这个问题的最佳方法。

当调用 terminate 时,它​​确实杀死了子进程,但随后它变成了一个已失效的僵尸,直到进程对象消失才会释放。下面的示例代码中循环永远不会结束的地方可以杀死它并在再次调用时允许重生,但似乎不是解决这个问题的好方法(即在 __init__() 中使用 multiprocessing.Process() 会更好)。

有人有什么建议吗?

class Process(object):
    def __init__(self):
        self.thing = Thing()
        self.running_flag = multiprocessing.Value("i", 1)

    def run(self):
        self.process = multiprocessing.Process(target=self.thing.worker, args=(self.running_flag,))
        self.process.start()
        print self.process.pid

    def pause_resume(self):
        self.running_flag.value = not self.running_flag.value

    def terminate(self):
        self.process.terminate()

class Thing(object):
    def __init__(self):
        self.count = 1

    def worker(self,running_flag):
        while True:
            if running_flag.value:
                self.do_work()

    def do_work(self):
        print "working {0} ...".format(self.count)
        self.count += 1
        time.sleep(1)

【问题讨论】:

  • 我认为直接的答案是您不想终止您的进程 - 因为您发现它们变成了可能会占用内存或谁知道的僵尸。在我有限的多处理经验中,我对让工人能够优雅地死去更加警惕。 this answer 的主题基本相同吗?
  • 不完全。我已经阅读了许多这样的线程,他们预计会发生异常,然后您可以处理它。我有一些东西正在运行但挂起的情况。操作员可能会发现某些东西运行不正确,我想要一种方法来停止该过程。由于它被挂起,它不会响应诸如通过 multiprocessing.Value() 发送停止标志之类的东西。很可能,他们会做的是杀死整个程序,然后重新启动它,所以我想知道在程序中是否有一个好的方法可以让子进程恢复。
  • 我想我知道你的意思,但我找到的唯一答案仍然如上 - 你只需要尽你所能避免“挂起”(例如,一个包罗万象的异常包装每个进程)。作为进一步的证据,在许多情况下线程可以说是更简单的解决方案,it has the same advice。在这一点上,以防万一您还没有检查 - 确保您确实需要多处理而不是线程,因为每个都有自己的优点和缺点。

标签: python multiprocessing python-multiprocessing


【解决方案1】:

您可以在后台将子进程作为守护进程运行。

process.daemon = True

守护进程中的任何错误和挂起(或无限循环)都不会影响主进程,只有在主进程退出后才会终止。

这将适用于简单的问题,直到您遇到许多子守护进程,这些子守护进程将不断从父进程获取内存而没有任何显式控制。

最好的方法是设置一个Queue 让所有子进程与父进程通信,这样我们就可以join 很好地清理它们。下面是一些简单的代码,用于检查子进程是否挂起(又名time.sleep(1000)),并向队列发送消息,让主进程对其采取行动:

import multiprocessing as mp
import time
import queue

running_flag = mp.Value("i", 1)

def worker(running_flag, q):
    count = 1
    while True:
        if running_flag.value:
            print "working {0} ...".format(count)
            count += 1
            q.put(count)
            time.sleep(1)
            if count > 3:
                # Simulate hanging with sleep
                print "hanging..."
                time.sleep(1000)

def watchdog(q):
    """
    This check the queue for updates and send a signal to it
    when the child process isn't sending anything for too long
    """
    while True:
        try:
            msg = q.get(timeout=10.0)
        except queue.Empty as e:
            print "[WATCHDOG]: Maybe WORKER is slacking"
            q.put("KILL WORKER")

def main():
    """The main process"""
    q = mp.Queue()

    workr = mp.Process(target=worker, args=(running_flag, q))
    wdog = mp.Process(target=watchdog, args=(q,))

    # run the watchdog as daemon so it terminates with the main process
    wdog.daemon = True

    workr.start()
    print "[MAIN]: starting process P1"
    wdog.start()
    
    # Poll the queue
    while True:
        msg = q.get()
        if msg == "KILL WORKER":
            print "[MAIN]: Terminating slacking WORKER"
            workr.terminate()
            time.sleep(0.1)
            if not workr.is_alive():
                print "[MAIN]: WORKER is a goner"
                workr.join(timeout=1.0)
                print "[MAIN]: Joined WORKER successfully!"
                q.close()
                break # watchdog process daemon gets terminated

if __name__ == '__main__':
    main()

如果不终止worker,尝试将join() 连接到主进程将永远阻塞,因为worker 从未完成。

【讨论】:

  • 我认为您的代码中有错字。不应该是if msg == "KILL WORKER":
【解决方案2】:

Python 多处理处理进程的方式有点混乱。

来自多处理指南:

加入僵尸进程

在 Unix 上,当一个进程完成但尚未加入时,它就变成了僵尸。永远不应该有很多,因为每次新进程启动(或调用 active_children())时,所有尚未加入的已完成进程都将加入。同时调用已完成进程的 Process.is_alive 将加入该进程。即便如此,明确加入您启动的所有流程可能是一种好习惯。

为了避免进程变成僵尸,你需要在杀死它后调用它的join()方法。

如果您想要一种更简单的方法来处理系统中的挂起呼叫,您可以查看pebble

【讨论】:

  • 那么是否仍然需要调用process.terminate() 以确保进程已终止,或者仅join() 就足以杀死它们?
  • terminate 通过SIGTERM 信号向目标进程发出终止请求。是否接受请求取决于流程(在大多数情况下他们会这样做)。如果该过程已经结束,则它无效。 join 只是指示操作系统在进程结束时回收进程资源,否则它将一直阻塞到那时。 join 必须始终在已结束的进程上调用,否则操作系统将堆积耗尽进程的资源。它们通常被称为“僵尸”进程,因为它们实际上是曾经运行过的空壳。
【解决方案3】:

(没有足够的声誉点发表评论,特此完整答案)

@PieOhPah:感谢您提供这个非常好的示例。
不幸的是,只有一个小缺陷不会让看门狗杀死工人:

if msg == "KILL WATCHDOG":

应该是:

if msg == "KILL WORKER":

所以代码变成了(为python3更新了打印):

import multiprocessing as mp
import time
import queue

running_flag = mp.Value("i", 1)

def worker(running_flag, q):
    count = 1
    while True:
        if running_flag.value:
            print ("working {0} ...".format(count))
            count += 1
            q.put(count)
            time.sleep(1)
            if count > 3:
                # Simulate hanging with sleep
                print ("hanging...")
                time.sleep(1000)

def watchdog(q):
    """
    This check the queue for updates and send a signal to it
    when the child process isn't sending anything for too long
    """
    while True:
        try:
            msg = q.get(timeout=10.0)
        except queue.Empty as e:
            print ("[WATCHDOG]: Maybe WORKER is slacking")
            q.put("KILL WORKER")

def main():
    """The main process"""
    q = mp.Queue()

    workr = mp.Process(target=worker, args=(running_flag, q))
    wdog = mp.Process(target=watchdog, args=(q,))

    # run the watchdog as daemon so it terminates with the main process
    wdog.daemon = True

    workr.start()
    print ("[MAIN]: starting process P1")
    wdog.start()

    # Poll the queue
    while True:
        msg = q.get()
#        if msg == "KILL WATCHDOG":
        if msg == "KILL WORKER":
            print ("[MAIN]: Terminating slacking WORKER")
            workr.terminate()
            time.sleep(0.1)
            if not workr.is_alive():
                print ("[MAIN]: WORKER is a goner")
                workr.join(timeout=1.0)
                print ("[MAIN]: Joined WORKER successfully!")
                q.close()
                break # watchdog process daemon gets terminated

if __name__ == '__main__':
    main()

【讨论】:

  • 我想知道为什么上面的答案没有if msg == "KILL WORKER" 检查 - 感谢您关闭循环。
【解决方案4】:

只需输入文件名而不是train_model_parallel

kill -9 `ps -ef | grep train_model_parallel.py | grep -v grep | awk '{print $2}'`

【讨论】:

  • 这在我看来不像是 python 代码,对吧?
  • 这只是话题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-30
  • 2018-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-24
  • 1970-01-01
相关资源
最近更新 更多