python-3.x 为什么当另一个线程处于活动状态时,全局对象上不调用__del__?

xxhby3vn  于 2023-03-24  发布在  Python
关注(0)|答案(3)|浏览(164)

我正在做一个项目,它的结构与下面的代码类似。我希望有一个对象,它在创建时打开一个线程,并在对象被销毁时自动关闭它。当对象在函数中示例化时,这可以按预期工作,但是当对象在全局范围内创建时,__del__没有被调用,导致程序挂起。

import threading

def thread(event):
    event.wait()
    print("Terminating thread")

class ThreadManager:
    def __init__(self):
        self.event = threading.Event()
        self.thread = threading.Thread(target=thread, args=(self.event,))
        self.thread.start()

    def __del__(self):
        self.event.set()
        print("Event set")
        self.thread.join()

if __name__ == '__main__':
    print("Creating thread")
    manager = ThreadManager()
    #del manager

除非我显式删除manager对象,否则程序将挂起。我假设解释器正在等待删除全局对象,直到所有非守护进程线程完成,从而导致死锁情况。
我的问题是,有人可以证实这一点,并提供一个解决方案(我已经阅读了this页面,所以我不是在寻找一个使用close()函数或类似的解决方案,我只是好奇地听到一个替代方案,将执行自动清理的想法),或者反驳它,告诉我我做错了什么?

d4so4syb

d4so4syb1#

与C++等语言不同,Python不会在对象超出作用域后立即销毁对象,这就是__del__不可靠的原因。你可以在这里阅读更多:https://stackoverflow.com/a/35489349/5971137
至于解决方案,我认为这是上下文管理器(with)的完美案例:

>>> import threading
>>>
>>> def thread(event):
...     event.wait()
...     print("Terminating thread")
...
>>> class ThreadManager:
...     def __init__(self):
...             print("__init__ executed")
...             self.event = threading.Event()
...             self.thread = threading.Thread(target=thread, args=(self.event,))
...             self.thread.start()
...     def __enter__(self):
...             print("__enter__ executed")
...             return self
...     def __exit__(self, *args):
...             print("__exit__ executed")
...             self.event.set()
...             print("Event set")
...             self.thread.join()
...
>>> with ThreadManager() as thread_manager:
...     print(f"Within context manager, using {thread_manager}")
...
__init__ executed
__enter__ executed
Within context manager, using <__main__.ThreadManager object at 0x1049666d8>
__exit__ executed
Event set
Terminating thread

print语句显示执行顺序按照您希望的方式工作,其中__exit__现在是您可靠的清理方法。

kupeojn6

kupeojn62#

这种行为实际上在object.__del__()的官方文档中特别提到:
不能保证为解释器退出时仍然存在的对象调用__del__()方法。

jq6vz3qz

jq6vz3qz3#

用途:

self.thread = threading.Thread(target=thread, args=(self.event,), daemon=True)

这不会阻止进程的退出。

相关问题