sys.exit()不工作,没有其他线程,没有尝试:块捕获SystemExit?

时间:2016-04-12 03:46:43

标签: python multithreading

我无法退出我的Python应用。在调用sys.exit()之后,python.exe继续运行,我必须使用任务管理器将其终止。

过去4个小时我一直在研究这个问题,而且我很难过。

这是Windows 10 x86上的Python 3.4.4。

首先,我有一个多线程应用程序。但是,在调用sys.exit()之前,我已经验证所有线程都在退出,只有主线程在运行。 (我这样做是通过在while循环中调用threading.enumerate()并等到只剩下主线程,打印正在运行的线程列表并观察它在每个循环中变小,直到只剩下主线程。 )

另外,我已经确认我没有在try:block中包含任何吞噬SystemExit异常的块。如果我打印sys.exc_info()我得到(None,None,None),如果我调用raise,那么它也确认没有异常待处理。

有趣的是,我通过评论我的应用程序的不同部分逐一禁用每个帖子,将其缩小到违规线程。 (我总共有4个帖子,每个都做不同的事情。)

如果我评论出有问题的帖子,我可以退出我的应用程序没问题。但同样,即使我运行该线程,该线程也成功退出,那里只有一些阻止主要Python exe退出的东西。

我已尝试设置守护程序标志,但这样做无论如何都无法做到。违规线程的目的是在PriorityQueue()等待1秒超时,然后当超时时检查threading.Event()标志以正常退出自身。再次,这很好。

,我可以在我的while()循环中看到该程序正在退出该线程正在运行,然后停止。

唯一的其他信息是此应用程序是通过console_scripts条目启动的。我查看了setuptools创建的脚本文件,看到它只是将调用包装到sys.exit()中的入口点,但即使是黑客攻击该文件,我也无法退出。

我试过调用sys.exit,引发SystemExit,然后返回让console_script调用sys.exit。这些都不起作用。

我也尝试过更多暴力行为,比如os._exit(),但这也没有用。

真正奇怪的是,如果我创建一个递归循环(一个只调用自身的简单单行方法),并且在我设置阻止线程的线程事件之前将其放在我的stop方法中,然后Python将按原样退出。 (我错误地做了那个,并且首先是傻眼了,但是如果我在调用sys.exit之前将那个循环调用了几行,那么递归循环不会杀死python.exe。所以即使我的问题线程正常退出,有关它试图退出的事情导致Python.exe挂起。

所以,我的问题是,有没有人有任何其他的想法或事情可以尝试Python为什么不退出?特别是为什么我的问题线程停止,只有主线程仍然存在,但sys.exit或os._exit()什么都不做?我完全难过了。

我的应用程序消耗大约90MB的内存,在任务管理器中,我可以看到GC正在完成其工作,就像我的应用程序是"挂起"在sys.exit()调用之后,我看到内存使用量在大约30秒的时间内从90MB下降到0.1MB。但即使离开它之后,python.exe也不会停止。

更新:这里有一些代码,用于演示内容:

从注册为console_script的模块和功能:

  def run_from_command_line(args=None):
        path = os.path.abspath(os.path.curdir)
        CommandLineUtility(path).execute()

从启动我的应用程序的CommandLineUtility()。这是最后一行:

  def __init__(...):
      ... skipping a bunch of setup stuff
      MpfMc(options=vars(args), config=mpf_config,
            machine_path=machine_path).run()  # this is not a threading run, just the name of the method for my app

来自MpfMc():

def __init__(...):
    ...
    self.thread_stopper = threading.Event()
    ...
    self.asset_manager = AssetManager(self)

来自AssetManager():

    self.loader_thread = AssetLoader(loader_queue=self.loader_queue,
                                     loaded_queue=self.loaded_queue,
                                     exception_queue=self.machine.crash_queue,
                                     thread_stopper=self.machine.thread_stopper)
    self.loader_thread.daemon = True
    self.loader_thread.start()

来自AssetLoader:

def run(self):
    """Run loop for the loader thread."""
    while True:
        try:
            asset = self.loader_queue.get(block=True, timeout=1)
        except Empty:
            asset = None

        if self.thread_stopper.is_set():
            return

        if asset:
            if not asset.loaded:
                with asset.lock:
                    asset.do_load()
            self.loaded_queue.put(asset)

从停止app的MpfMc.stop()方法:

def stop(self):
    self.log.info("Stopping ...")

    self.thread_stopper.set()

    while [x for x in self.threads if x.is_alive()]:
        # self.threads is a list of threads I created, not the main thread.
        print("Waiting for threads to stop")
        print([x for x in self.threads if x.is_alive()])
        print(threading.enumerate())
        time.sleep(0.5)

    for thread in self.threads:
        # verify none of the sub threads are alive
        print("THREAD", thread, thread.is_alive())

    sys.exit()  # here's where I also tried raise SystemExit, os._exit(), etc

谢谢!

0 个答案:

没有答案