【问题标题】:python multiprocessing.Process.terminate - How to kill child processespython multiprocessing.Process.terminate - 如何杀死子进程
【发布时间】:2017-01-30 14:31:53
【问题描述】:

这段代码:

import multiprocessing as mp
from threading import Thread
import subprocess
import time

class WorkerProcess(mp.Process):
  def run(self):
      # Simulate long running task
      self.subprocess = subprocess.Popen(['python', '-c', 'import time; time.sleep(1000)'])
      self.code = self.subprocess.wait()


class ControlThread(Thread):
  def run():
      jobs = []
      for _ in range(2):
          job = WorkerProcess()
          jobs.append(job)
          job.start()

      # wait for a while and then kill jobs
      time.sleep(2)
      for job in jobs:
          job.terminate()

if __name__ == "__main__":
    controller = ControlThread()
    controller.start()

当我终止生成的 WorkerProcess 实例时。他们死得很好,但是子进程python -c 'import time; time.sleep(1000) 一直运行到完成。这是well documented in the official docs,但是如何杀死被杀死进程的子进程?

一个可能的灵魂可能是:

  • 在 try/except 块中包装 WorkerProcess.run() 方法,捕获 SIGTERM,并终止 subprocess.call call。但我不确定如何在 WorkerProcess 中捕获 SIGTERM

  • 我也尝试在 WorkerProcess 中设置 signal.signal(signal.SIGINT, handler),但我得到 ValuError,因为它只允许在主线程中设置。

我现在该怎么办?

【问题讨论】:

    标签: python python-multithreading python-multiprocessing


    【解决方案1】:

    编辑:正如@svalorzen 在 cmets 中指出的那样,这实际上不起作用,因为对 self.subprocess 的引用丢失了。


    终于找到了一个干净、可以接受的解决方案。由于 mp.Process.terminate 是一个方法,我们可以覆盖它。

    class WorkerProcess(mp.Process):
        def run(self):
            # Simulate long running task
            self.subprocess = subprocess.Popen(['python', '-c', 'import time; time.sleep(1000)'])
            self.code = self.subprocess.wait()
    
        # HERE
        def terminate(self):
          self.subprocess.terminate()
          super(WorkerProcess, self).terminate()
    

    【讨论】:

    • 由于某种原因,当我调用terminate() 函数时,我丢失了self.subprocess 字段,因为类中不再包含该字段(通过打印dir 进行检查)。你知道为什么吗?
    • 奇怪..我也有AttributeError,所以我在WorkerProcess.__init__中设置了self.subprocess = None(别忘了打电话给super())。有了它,我不再得到AttributeError,但值始终是None:在开始之后和终止之前。但是在WorkerProcess.__init__ 中设置self.bar = 42 可以按预期工作(可以随时访问)。我认为这需要一个单独的问题(如果您提出问题,请随时发布链接)
    • 是的,我发现了。但我想这作为一个公认的答案是没有意义的,因为它不起作用。我认为它有效,因为听起来您实际上使用此代码来解决您的问题,但如果它没有..
    • 它解决了我在终止父进程时终止子进程的问题,这就是我真正关心的。您是否介意首先分享一下为什么需要访问self.subprocess
    • 我不明白。在terminate 中,您明确调用self.subprocess.terminate()。因此,您也在访问self.subprocess。我不能调用子进程的terminate,因为worker的terminate函数中不存在该字段,你刚才说如果我理解正确你也有同样的问题。因此,当您无法调用其终止函数时,您如何终止子进程?
    【解决方案2】:

    您可以使用队列向您的子进程发送消息,并在退出自己之前很好地要求它们终止子进程。除了主线程之外,您不能在其他任何地方使用信号,因此信号不适合这种情况。

    奇怪的是,当我像这样修改代码时,即使我用 control+C 中断它,子进程也会死掉。不过,这可能与操作系统相关。

    import multiprocessing as mp
    from threading import Thread
    import subprocess
    import time
    from Queue import Empty
    
    
    class WorkerProcess(mp.Process):
        def __init__(self,que):
            super(WorkerProcess,self).__init__()
            self.queue = que
    
        def run(self):
            # Simulate long running task
            self.subprocess = subprocess.Popen(['python', '-c', 'import time; time.sleep(1000)'])
            while True:
                a = self.subprocess.poll()
                if a is None:
                    time.sleep(1)
                    try:
                        if self.queue.get(0) == "exit":
                            print "kill"
                            self.subprocess.kill()
                            self.subprocess.wait()
                            break
                        else:
                            pass
                    except Empty:
                        pass
                    print "run"
                else:
                    print "exiting"
    
    
    class ControlThread(Thread):
    
        def run(self):
            jobs = []
            queues = []
            for _ in range(2):
                q = mp.Queue()
                job = WorkerProcess(q)
                queues.append(q)
                jobs.append(job)
                job.start()
    
            # wait for a while and then kill jobs
            time.sleep(5)
            for q in queues:
                q.put("exit")
            time.sleep(30)
    
    if __name__ == "__main__":
        controller = ControlThread()
        controller.start()
    

    希望这会有所帮助。

    汉努

    【讨论】:

    • 您好,抱歉耽搁了时间,感谢您的回答,但我想避免使用队列。根据正确终止的示例,您是对的。这只是一个虚拟示例,只是为了说明工作流程,它(idk 为什么)有效(不明智地选择,我同意)。在我的真实案例中,该过程一直在运行
    猜你喜欢
    • 2010-12-08
    • 1970-01-01
    • 2011-09-26
    • 2014-06-19
    • 1970-01-01
    • 2015-04-17
    • 1970-01-01
    相关资源
    最近更新 更多