我有一个自定义线程子类,它重复调用“绑定”对象上的方法。目标是每当“绑定”对象被 GC 时自动加入该线程:
from threading import Thread, Event
from weakref import ref, WeakSet
from typing import Callable, Generic
from typing_extensions import TypeVar
import atexit
T = TypeVar("T", bound=object)
_workers: WeakSet = WeakSet()
@atexit.register
def stopAllWorkers():
# copy because _workers will be mutated during enumeration.
refs = list(_workers)
for worker in refs:
try:
worker.stop()
except:
pass
class MaintenanceWorker(Thread, Generic[T]):
def __init__(self, bound_to: T, interval: float, task: Callable[[T], None]):
self._interval = interval
self._task = task
self._ref = ref(bound_to, lambda _: self.stop())
self._finished = Event()
super().__init__()
def stop(self):
self._finished.set()
_workers.discard(self)
def run(self):
self._finished.clear()
_workers.add(self)
while True:
self._finished.wait(self._interval)
if self._finished.is_set() or (subject := self._ref()) is None:
_workers.discard(self)
break
try:
self._task(subject)
except Exception:
pass
finally:
del subject
以下类的实例是否能够被垃圾收集,因为它们的成员之一是正在运行的线程?
class Foo:
def __init__(self):
self._worker = MaintenaceWorker(bound_to=self, interval=15*60.0, Foo.bar)
self._worker.start()
def bar(self):
# some convoluted logic
...
简单的答案是不,
self._worker = MaintenaceWorker(bound_to=self, interval=15*60.0, Foo.bar)
stores self ^^^^^^^
class MaintenanceWorker(Thread, Generic[T]):
def __init__(self, bound_to: T, interval: float, task: Callable[[T], None]):
self._interval = interval
self._task = task
^^^^^^^^^^^^^^^^^ stores self
如果您调用
task
而不是存储它,那么“函数”应该一直存在,直到它返回并且调用者失去对它的引用,因此该对象不会被垃圾收集。