一个线程安全的memoize装饰器

时间:2012-10-26 02:05:58

标签: python multithreading decorator memoization gil

我正在尝试制作一个适用于多个线程的memoize装饰器。

我明白我需要将缓存用作线程之间的共享对象,并获取/锁定共享对象。我当然正在推出主题:

for i in range(5):
            thread = threading.Thread(target=self.worker, args=(self.call_queue,))
            thread.daemon = True
            thread.start()

工人是:

def worker(self, call):
    func, args, kwargs = call.get()
    self.returns.put(func(*args, **kwargs))
    call.task_done()

当然,当我将一个用备忘录函数(如this)修饰的函数同时发送到许多线程时,问题就开始了。

如何将备忘录的缓存实现为线程间的共享对象?

1 个答案:

答案 0 :(得分:2)

最直接的方法是对整个缓存使用单个锁,并要求对缓存的任何写入首先获取锁。

在您发布的示例代码中,在第31行,您将获取锁定并检查结果是否仍然缺失,在这种情况下,您将继续计算并缓存结果。像这样:

lock = threading.Lock()
...
except KeyError:
    with lock:
        if key in self.cache:
            v = self.cache[key]
        else:
            v = self.cache[key] = f(*args,**kwargs),time.time()

您发布的示例将每个函数的缓存存储在字典中,因此您还需要为每个函数存储一个锁。

如果你在一个备受争议的环境中使用这个代码,那么它可能是无法接受的低效率,因为线程必须等待彼此,即使它们没有计算相同的东西。您可以通过在缓存中存储每个锁的锁来改进这一点。但是,您还需要全局锁定对锁存储的访问,否则在创建每个密钥锁时会出现竞争条件。