【问题标题】:Python uncaught exceptions in main not killing threads [duplicate]Python在main中未捕获的异常不会杀死线程[重复]
【发布时间】:2021-04-27 05:48:59
【问题描述】:

当我运行 python 程序(2.7 或 3)时,我导入了一个模块,该模块具有一个初始化一些线程的类。问题是每当主线程中发生未捕获的异常时,主函数就会死掉,但线程会像僵尸一样继续运行,导致 python 进程永远不会死。

在主线程(甚至其他线程)中有任何未捕获的异常以杀死所有地方的所有内容的最佳方法是什么。

由于我经常调用subprocess模块,所以我通常使用threading.Event来帮助干净地退出。但是,未捕获的异常不会触发这些事件。

这是一个线程不会死掉的程序示例......

prog1.py

#!/usr/bin/env python2.7

import threading
import modx1

mod_obj = modx1.Moddy()

raise Exception('DIE UNEXPECTEDLY')

try:
    raise Exception('known problem here')
except Exception:
    mod_obj.kill_event.set()

modx1.py

#!/usr/bin/env python2.7

import threading
import subprocess
from time import sleep


class Moddy():
    def __init__(self):
        self.kill_event = threading.Event()
        self.my_thread=threading.Thread(target=self.thread_func)
        self.my_thread.start()
    def thread_func(self):
        while  not self.kill_event.is_set():
            print('thread still going....')
            sleep(2)

【问题讨论】:

  • stackoverflow.com/questions/2564137/…也许守护进程标志是你想要的?
  • 我看过了。我不需要守护线程和/或使用事件的一般概念。在那篇文章中没有直接解决我的具体问题。虽然代码本身没有显示它,但我不能使用守护线程,因为我的很多代码中都有所有派生的进程。
    对于我在这里接受的答案,我确实看到了解决方案(“threading. main_thread().is_alive()") 在该帖子底部的一个不被接受的答案中。但是,同样,这篇文章不是那个的重复。

标签: python multithreading exception


【解决方案1】:

这似乎很老套,但还是要开始。

prog1.py - 我添加了 5 秒的睡眠时间,因此您可以看到线程正在运行,然后完成。

import modx1
import time


mod_obj = modx1.Moddy()
time.sleep(5)
raise Exception('DIE UNEXPECTEDLY')

try:
    raise Exception('known problem here')
except Exception:
    mod_obj.kill_event.set()

modx1.py -> 检查您设置的布尔值以及主线程的状态。

import ctypes
import threading
import subprocess
from time import sleep


class Moddy():
    def __init__(self):
        self.kill_event = threading.Event()
        self.my_thread = threading.Thread(target=self.thread_func)
        self.my_thread.start()

    def thread_func(self):
        while not self.kill_event.is_set() and threading.main_thread().is_alive():
            print('thread still going....')
            sleep(2)

输出

thread still going....
thread still going....
thread still going....
Traceback (most recent call last):
  File "/Users/gaurishankarbadola/PycharmProjects/untitled1/prog1.py", line 8, in <module>
    raise Exception('DIE UNEXPECTEDLY')
Exception: DIE UNEXPECTEDLY

Process finished with exit code 1

【讨论】:

  • threading.main_thread.is_alive() !!我不知道那是一回事!谢谢!这就是我需要的!
  • 考虑到我使用线程事件来干净地关闭一切,我开始认为threading.main_thread().is_alive() 可以轻松取代任何此类事件......
  • @MookiBar 是的,这可能是一种可能性,但有时您的主线程可能正在运行,而您仍想终止子线程,在这种情况下,您的 self.kill_event.is_set() 逻辑是正确的。跨度>
【解决方案2】:

假设您不依赖线程中发生的清理,最简单的解决方案是将所有线程作为守护线程启动; Python 程序在所有非守护线程都完成后退出,因此如果您的主线程是唯一的非守护线程,则程序将在主线程完成时终止。

所需的更改是微不足道的,只需更改:

self.my_thread=threading.Thread(target=self.thread_func)

到:

self.my_thread=threading.Thread(target=self.thread_func, daemon=True)

线程将作为守护进程启动。

如果您确实有必须进行的清理并且在程序终止时不是自动的(例如,通过网络向远程机器发送一些“我完成了”的消息,无论出于何种原因,简单地关闭连接都是不够的),daemon 将不起作用(线程被强制终止,无论它们在中间),所以你可以使用你的方法,你只需要扩展 try 块以覆盖所有代码线程被启动,所以处理总是发生。我还建议 set-ing Eventfinally 块中,而不是 except 块中,如果您想始终告诉线程进行清理,而不仅仅是在发生不好的事情时。如果它应该只在异常情况下完成,我会使用一个裸except: 块,并在设置Event 后明确地重新raise,例如:

try:
    # Entire program after thread launches
except:
    mod_obj.kill_event.set()
    raise  # Reraises exception as if it were never caught, rather than silencing it

【讨论】:

  • 嘿,就像我写的评论一样。很高兴知道它是适用的,可悲的是我的 threading-fu 缺乏。
  • 不幸的是,守护线程并不理想。就像我说的,我经常使用“子进程”,而守护线程会以不干净的方式杀死它们(僵尸进程)。
  • 同样不幸的是,后半部分意味着我必须将整个代码放入 try/except 块中。难道没有类似于旗帜或信号的东西可以解决问题吗?
  • @MookiBar:您可以在sys.excepthook 中放置一个处理程序来完成相同的工作。缺陷在于它是一个单一的全局处理程序,并且一些第三方库也可能依赖它,如果所有覆盖程序都不小心保留他们替换的钩子并在完成时调用它,则会导致冲突。不过,将所有代码放在try 块中并不难。如果您讨厌额外的缩进,请将 try 块中的代码放在另一个函数中,并让 try 块的内容调用该函数。
  • 好点。我倾向于将强制性的if __name__ == "__main__" 之后的所有内容都放在一个单独的函数中,所以这不会是世界上最糟糕的事情。我刚刚让其他人看不起我 try/excepting 我的整个代码...
猜你喜欢
  • 2011-06-30
  • 2012-11-10
  • 2011-12-05
  • 1970-01-01
  • 2020-06-13
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 2015-08-07
相关资源
最近更新 更多