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

Python sys.exit()不工作,没有其他线程,也没有尝试:阻止捕获SystemExit?,python,multithreading,Python,Multithreading,我无法退出我的Python应用程序。调用sys.exit()后,python.exe将保持运行,我必须使用任务管理器终止它 我花了4个小时来研究这个问题,我很困惑 这是Windows10x86上的Python 3.4.4 首先,我有一个多线程应用程序。但是,在调用sys.exit()之前,我已经验证了所有线程都在退出,只有主线程在运行。(我是通过在while循环中调用threading.enumerate()来实现的,并一直等到只剩下主线程,打印正在运行的线程列表,然后看着它在每个循环中变小,直

我无法退出我的Python应用程序。调用sys.exit()后,python.exe将保持运行,我必须使用任务管理器终止它

我花了4个小时来研究这个问题,我很困惑

这是Windows10x86上的Python 3.4.4

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

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

有趣的是,我已经缩小了这个范围,通过注释我的应用程序的不同部分,一个接一个地禁用每一个线程,来解决问题。(我总共有4个线程,每个线程做不同的事情。)

如果我注释掉有问题的帖子,我可以毫无问题地退出我的应用程序。但同样,即使当我运行该线程时,该线程也会成功退出,其中有一些东西会阻止主Python exe退出

我已经尝试设置守护进程标志,但这两种方式都没有效果。有问题的线程的目的是在优先级队列()处等待1秒超时,然后在超时时检查threading.Event()标志以正常退出。同样,这很好。我可以在程序退出时的while()循环中看到该线程正在运行,然后停止

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

我尝试调用sys.exit,引发SystemExit,然后简单地返回,让控制台脚本调用sys.exit。这些都不管用

我还尝试了更多的暴力手段,比如os.\u exit(),但这也不起作用

真正奇怪的是,如果我创建一个递归循环(一个简单的单行方法,只调用自身),在设置线程事件停止线程之前,我将它放在stop方法中,那么Python将按应有的方式退出。(我犯了一个错误,第一次这样做时,我目瞪口呆。但是如果我在调用sys.exit之前将循环调用向下移动几行,那么递归循环不会杀死python.exe。因此,即使我的问题线程正确退出,但它试图退出的某些内容会导致python.exe挂起

那么,我的问题是,有没有人有任何其他想法或事情可以尝试为什么Python不会退出?特别是为什么我的问题线程停止,只剩下主线程,而sys.exit或os.\u exit()什么都不做?我完全被难倒了

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

更新:以下是一些代码,演示了事物的外观:

从注册为console_脚本的模块和函数:

  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()开始:

从AssetManager()开始:

从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)
从停止应用程序的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

谢谢!

我们需要看一些演示问题的代码。你说你试过注释一堆东西。剩下的短到可以发布吗?如果没有,继续删除那些不能解决问题的部分,直到剩下的短到可以发布,然后发布。好的,谢谢。我刚刚添加了一堆小代码ts,希望这足够了?我想它演示了我所说的一切?在
stop
方法中,在
sys.exit
之前有一个
for
循环,注释是“验证只有主线程是活动的”调用每个线程的
is\u alive
方法的print语句。这意味着您希望
main\u thread.is\u alive()
返回True。但如果是这种情况,则前面的
while
循环将永远不会结束,因为活动线程列表永远不会为空-它将始终包含主线程。你能澄清一下吗?很好。注释是错误的。(我在粘贴到SO时添加了注释。)
self.threads
是我创建的线程列表,因此主线程不在其中。因此,该注释是错误的。我将更新帖子。谢谢!那么
while
循环是否终止/for循环是否产生输出?
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)
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