defcall(self, i): x = [] try: future = self.executor.submit(self.fun, i) res = future.result(timeout=1) except Exception as e: print("-"*20) return x
defpredict(): f = Foo() for i inrange(3): f.call(i)
if __name__ == "__main__": with ProcessPoolExecutor(max_workers=2, mp_context=multiprocessing.get_context("spawn")) as e: futures = e.submit(predict) futures.result()
肉眼观察 乍一看这段代码是没有任何问题的,在 submit 之后,获取 result
,并且用 try ... except ... 来捕获可能出现的任何情况。尤其是,当不使用
main 函数中的进程池时,直接普通的使用 predict
函数,这段代码不会引发任何问题,可以正常的退出。
defpredict(): f = Foo() for i inrange(3): f.call(i) print(gc.get_referrers(f)) print(sys.getrefcount(f)) print(sys.getrefcount(f.executor))
此时输出会多一行,形如
1
[<frame at 0x10c266240, file 'XXX', line 24, code call>, <frame at 0x10c6b0240, file 'XXX', line 24, code call>, <frame at 0x10c6b0440, file 'XXX', line 24, code call>]
defresult(self, timeout=None): """Return the result of the call that the future represents. Args: timeout: The number of seconds to wait for the result if the future isn't done. If None, then there is no limit on the wait time. Returns: The result of the call that the future represents. Raises: CancelledError: If the future was cancelled. TimeoutError: If the future didn't finish executing before the given timeout. Exception: If the call raised then that exception will be raised. """ try: withself._condition: ifself._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: raise CancelledError() elifself._state == FINISHED: returnself.__get_result()
self._condition.wait(timeout)
ifself._state in [CANCELLED, CANCELLED_AND_NOTIFIED]: raise CancelledError() elifself._state == FINISHED: returnself.__get_result() else: raise TimeoutError() finally: # Break a reference cycle with the exception in self._exception self = None
解决方案
消除中间变量 future
1 2 3 4
future = self.executor.submit(self.fun, i) res = future.result(timeout=1) # 改为 res = self.executor.submit(self.fun, i).result(timeout=1)
defcall(self, i): x = [] try: future = self.executor.submit(self.fun, i) res = future.result(timeout=1) except Exception as e: print("-"*20) del future return x
Q & A
Q: 能不能在 Foo 中添加 del 函数解决这个问题 A: 在
Python 3.8.16 可以,Python 3.9.6 不行。可以手动增加 del
函数添加 print 函数,观察是否有对应的输出。