这个虚拟类类似于我当前的项目:
class EndProcess(object):
def __init__(self):
self._Q = multiprocessing.Queue()
self.p1 = multiprocessing.Process(target=self.worker, args=("p1",))
self.p2 = multiprocessing.Process(target=self.worker, args=("p2",))
self.p3 = multiprocessing.Process(target=self.worker, args=("p3",))
self.p1.start()
self.p2.start()
self.p3.start()
def worker(self, name):
while True:
rnd = random.randint(0, 100)
self._Q.put((name, rnd))
print(name, rnd)
time.sleep(1)
def __del__(self):
# "log" final state of Queue
while not self._Q.empty():
print(self._Q.get())
# free resources
...
# clean up workers
self.p1.terminate()
self.p2.terminate()
self.p3.terminate()
if __name__ == "__main__":
ep = EndProcess()
问题在于不能保证__del__
总是被调用,尤其是在重要的时间点,因为只有当对象的引用计数达到零时,Python 的垃圾收集器才会调用__del__
。
我如何确保资源始终免费,而工人始终被解雇?
我如何确保资源始终免费,而工作人员始终被终止?
您可以通过实现上下文管理器方法来实现此目的__enter__
和__exit__
。然后,可以将EndProcess
类与上下文管理器语句with
一起使用。这样,即使发生错误,清理逻辑也会执行:
class EndProcess(object):
def __init__(self):
self._Q = multiprocessing.Queue()
self.p1 = multiprocessing.Process(target=self.worker, args=("p1",))
self.p2 = multiprocessing.Process(target=self.worker, args=("p2",))
self.p3 = multiprocessing.Process(target=self.worker, args=("p3",))
self.p1.start()
self.p2.start()
self.p3.start()
def worker(self, name):
while True:
rnd = random.randint(0, 100)
self._Q.put((name, rnd))
print(name, rnd)
time.sleep(1)
def __enter__(self):
# Add your setup logic here. Initialize any data structures.
# set any threads, etc.
pass
def __exit__(self, exc_type, exc_value, traceback):
# The three arguments to `__exit__` describe the exception
# caused the `with` statement execution to fail. If the `with`
# statement finishes without an exception being raised, these
# arguments will be `None`.
#
# These arguments may be useful for things such as logging, or
# debugging.
while not self._Q.empty():
print(self._Q.get())
...
self.p1.terminate()
self.p2.terminate()
self.p3.terminate()
...
现在,您可以在上下文管理器语句中使用EndProcess
实例:
end_process = EndProcess()
...
with end_process as ep:
# Use ep like you would normally. `__enter__` and `__exit__` will be
# automatically.
pass