标准memoize装饰器中的缓存是否安全?
例如,假设我定义了以下装饰器:
import functools
def memoize(func):
cache = {}
@functools.wraps(func)
def memoized(*args):
result = None
if args in cache:
result = cache[args]
else:
result = func(*args)
cache[args] = result
return result
return memoized
并假设我正在尝试使用它来加速递归函数的计算,比如说:
@memoize
def fib(n):
result = 1
if n > 1:
result = fib(n-1) + fib(n-2)
return result
现在我想知道计算fib()的两个进程是否会发生冲突?例如:
if __name__ == "__main__":
from multiprocessing import Process
p1 = Process(target=fib, args=(19,))
p2 = Process(target=fib, args=(23,))
p1.start()
p2.start()
p1.join()
p2.join()
我的第一个想法是缓存是在fib的上下文中保存的,所以它是 在流程之间共享并可能导致竞争条件。但是之后, 我认为可能发生的最糟糕的事情是他们都会认为,比方说, fib(17)尚未计算,并将继续计算 并行并一个接一个地存储相同的结果 - 不理想, 但我觉得并不可怕。但我仍然想知道是否有办法以过程安全的方式做到这一点。
编辑:我在memoized()的每个分支中添加了一个print语句, 并且似乎每个进程都重新计算缓存中的所有fib值。 毕竟,缓存可能不是共享的?如果没有共享,我会更加聪明 如果有一种过程安全的方式来共享它(以节省更多的计算)。
答案 0 :(得分:0)
默认情况下,Python中的多进程程序在进程之间共享很少。共享的几件事是pickled
,它有自己的一些限制。示例中的fib
函数名义上是共享的,但pickle
按名称而不是按值存储函数。这就是为什么它的缓存不会被共享。
如果您希望为memoize
装饰器设置同步缓存,则需要向其添加同步,例如multiprocessing.Queue
或multiprocessing.Array
。这可能比简单地让每个进程重新计算值更慢,因为它会在进程来回传递更新时引入大量开销。
或者,如果您在运行时不需要单独的进程紧密同步,那么您可以提出一种方法,在进程启动和停止时将缓存传递给进程和从进程传递(例如,使用额外的进程)参数和返回值),以便顺序调用可以从memoization中受益,即使并行调用没有。