确保酸洗在 ProcessPoolExecutor.submit returns 之前完成
Ensure pickling is complete before ProcessPoolExecutor.submit returns
假设我有以下简单的 class(容易腌制):
import time
from concurrent.futures import ProcessPoolExecutor
class A:
def long_computation(self):
time.sleep(10)
return 42
我希望能够做到这一点:
a = A()
with ProcessPoolExecutor(1) as executor:
a.future = executor.submit(a.long_computation)
在 Python 3.6.9 上,此操作失败并显示 TypeError: can't pickle _thread.RLock objects
。在 3.8.0 上,它会导致无休止地等待获取锁。
什么有效(在两个版本上)是这样的:
a = A()
with ProcessPoolExecutor(1) as executor:
future = executor.submit(a.long_computation)
time.sleep(0.001)
a.future = future
在我看来,executor.submit
阻塞的时间不足以完成 a
的酸洗,并且在酸洗生成的 Future
对象时遇到问题。
我对 time.sleep(0.001)
解决方法不太满意,因为它涉及到一个神奇的数字,我想如果酸洗最终花费更长时间,它很容易失败。我不想睡得更安全、时间更长,因为那会是一种浪费。理想情况下,我希望 executor.submit
阻塞,直到可以安全地在 a
.
中存储对 Future
对象的引用为止
有更好的方法吗?
再考虑一下,我想到了以下几点:
import pickle
a = A()
with ProcessPoolExecutor(1) as executor:
a.future = executor.submit(pickle.loads(pickle.dumps(a)).long_computation)
它涉及重复工作,因为 a
被 pickle 两次,但工作正常并确保 Future
对象在任何情况下都不会按照需要被 pickle。
然后我意识到它起作用的原因是它创建了 a
的副本。因此可以通过在提交方法之前简单地(浅)复制对象来避免 pickling 和 unpickling,这确保副本上不存在对 Future
对象的引用:
from copy import copy
a = A()
with ProcessPoolExecutor(1) as executor:
a.future = executor.submit(copy(a).long_computation)
这比上面的 pickle 循环更快,也更不尴尬,但我仍然对这里的最佳实践感兴趣,所以我会稍等片刻再接受这个答案。
假设我有以下简单的 class(容易腌制):
import time
from concurrent.futures import ProcessPoolExecutor
class A:
def long_computation(self):
time.sleep(10)
return 42
我希望能够做到这一点:
a = A()
with ProcessPoolExecutor(1) as executor:
a.future = executor.submit(a.long_computation)
在 Python 3.6.9 上,此操作失败并显示 TypeError: can't pickle _thread.RLock objects
。在 3.8.0 上,它会导致无休止地等待获取锁。
什么有效(在两个版本上)是这样的:
a = A()
with ProcessPoolExecutor(1) as executor:
future = executor.submit(a.long_computation)
time.sleep(0.001)
a.future = future
在我看来,executor.submit
阻塞的时间不足以完成 a
的酸洗,并且在酸洗生成的 Future
对象时遇到问题。
我对 time.sleep(0.001)
解决方法不太满意,因为它涉及到一个神奇的数字,我想如果酸洗最终花费更长时间,它很容易失败。我不想睡得更安全、时间更长,因为那会是一种浪费。理想情况下,我希望 executor.submit
阻塞,直到可以安全地在 a
.
Future
对象的引用为止
有更好的方法吗?
再考虑一下,我想到了以下几点:
import pickle
a = A()
with ProcessPoolExecutor(1) as executor:
a.future = executor.submit(pickle.loads(pickle.dumps(a)).long_computation)
它涉及重复工作,因为 a
被 pickle 两次,但工作正常并确保 Future
对象在任何情况下都不会按照需要被 pickle。
然后我意识到它起作用的原因是它创建了 a
的副本。因此可以通过在提交方法之前简单地(浅)复制对象来避免 pickling 和 unpickling,这确保副本上不存在对 Future
对象的引用:
from copy import copy
a = A()
with ProcessPoolExecutor(1) as executor:
a.future = executor.submit(copy(a).long_computation)
这比上面的 pickle 循环更快,也更不尴尬,但我仍然对这里的最佳实践感兴趣,所以我会稍等片刻再接受这个答案。