我可以在 `__del__` 方法中使用线程 names/IDs 登录吗?

Can I log with thread names/IDs in `__del__` methods?

在Python标准库的logging库中,使用%(thread)%(threadName)宏(documented here) calls threading.current_thread (code),取出一个threading-内锁。

这个,as per the warnings in the __del__ documentation, causes a problem if you want to log inside __del__. We've run into a single-threaded deadlocking issue这个造成的

那么,我的问题是:是否有一种从 __del__ 方法 内部登录的始终安全的方法?在我们的例子中,记录有关由于其所有者被 GC 导致的连接被关闭的信息会很好,但这可能会在用户打开调试日志记录 [=30] 的情况下(可能不太可能)导致问题=]and 将线程信息添加到它们的调试配置中。我找到了一些关于 more explicit resource-sharing in __del__ 的帖子,但是 none 关于 stdlib 日志记录行为的帖子。

threading.current_thread()CPython stdlib version 实际上并没有获取锁。您 运行 遇到的问题是 eventlet 特有的,它会进行大量猴子修补以扰乱线程系统。虽然一种方法可能是停止使用 eventlet,但这可能需要您重写整个应用程序,并且它无法解决您可能最终在日志记录时尝试锁定的任何其他方式 - 例如,如果__str__ 方法原来需要一个锁。

__del__ 中登录或在 __del__ 中进行任何复杂工作的最安全方法可能是让 __del__ 发送消息告诉其他人代之以进行日志记录的代码。这会在 __del__ 和实际记录之间引入延迟,但这种延迟基本上是不可避免的,因为我们必须延迟记录,直到它需要的资源可用。也不会 gua运行tee 记录调用和 __del__ 发生在同一个线程上;在非 eventlet 上下文中,调用 current_thread() 来确定哪个线程正在处理 __del__ 可能是安全的,但是对于 eventlet,可能没有好的方法。

大多数发送消息的方式都会有类似的线程安全或重发运行cy 问题,但是Python 3.7 添加了 queue.SimpleQueue class 和 put方法设计为reent运行t。使用它来管理您的 __del__-logging 消息可能类似于

import queue
del_log_queue = queue.SimpleQueue()

def stop_del_logging():
    del_log_queue.put(None)

...

def __del__(self):
    del_log_queue.put((tuple, of, relevant, information))

...

# in some other thread

while True:
    info = del_log_queue.get()
    if info is None:
        break
    relevant_logger.log(do_something_with(info))

在非 eventlet 上下文中,让 logging.QueueHandler and logging.QueueListener 处理 SimpleQueue 可能是安全的,但对于 eventlet,这将不起作用,因为我们需要延迟创建 LogRecord 直到我们'重新退出 __del__.