python线程安全对象缓存

时间:2008-10-17 19:05:21

标签: python multithreading caching

我已经实现了一个python webserver。每个http请求都会生成一个新线程。 我需要在内存中缓存对象,因为它是一个Web服务器,我希望缓存是线程安全的。在python中是否存在线程安全对象缓存的标准实现?我找到了以下

http://freshmeat.net/projects/lrucache/

这看起来不是线程安全的。有人能指点我在python中实现线程安全缓存吗?

谢谢!

6 个答案:

答案 0 :(得分:9)

每个请求的线程通常是个坏主意。如果您的服务器遇到巨大的负载峰值,它将使盒子瘫痪。考虑使用在峰值使用期间可以增长到有限大小的线程池,并在负载较轻时缩小到较小的大小。

答案 1 :(得分:8)

默认情况下,Python中的许多操作都是线程安全的,因此标准字典应该没问题(至少在某些方面)。这主要是由于GIL,这将有助于避免一些更严重的线程问题。

这里有一个列表:http://coreygoldberg.blogspot.com/2008/09/python-thread-synchronization-and.html可能有用。

虽然这些操作的原子性质只是意味着如果你有两个线程同时访问字典,你就不会有完全不一致的状态。所以你不会有一个损坏的价值。但是,您(与大多数多线程编程一样)无法依赖这些原子操作的特定顺序。

所以简而言之......

如果您有相当简单的要求并且不打算写入缓存的内容的顺序,那么您可以使用字典并知道您将始终获得一致/未损坏的值(它可能只是过时了。)

如果你想确保在阅读和写作方面更加一致,那么你可能想看看Django的本地内存缓存:

http://code.djangoproject.com/browser/django/trunk/django/core/cache/backends/locmem.py

使用读/写锁进行锁定。

答案 2 :(得分:4)

您可能想要使用memcached。它非常快,非常稳定,非常受欢迎,具有良好的python库,并且如果您需要,它将允许您扩展到分布式缓存:

http://www.danga.com/memcached/

答案 3 :(得分:1)

对于线程安全对象,您需要threading.local:

from threading import local

safe = local()

safe.cache = {}

然后,您可以使用线程安全性在safe.cache中放置和检索对象。

答案 4 :(得分:0)

要点1 。GIL在这里无济于事,一个名为“存根”的(非线程安全)缓存的示例将是

stubs = {}

def maybe_new_stub(host):
    """ returns stub from cache and populates the stubs cache if new is created """
    if host not in stubs:
        stub = create_new_stub_for_host(host)
        stubs[host] = stub
    return stubs[host]

可能发生的是线程1调用maybe_new_stub('localhost'),并且发现我们在缓存中还没有该键。现在,我们切换到线程2,该线程调用相同的maybe_new_stub('localhost'),并且它还知道密钥不存在。因此,两个线程都调用create_new_stub_for_host并将其放入缓存。

地图本身受GIL保护,因此我们不能通过并发访问来破坏它。但是,缓存的逻辑不受保护,因此我们最终可能会创建两个或多个存根,并将除其中一个以外的所有存根都丢弃。

要点2。根据程序的性质,您可能不需要全局缓存。这样的共享缓存会强制所有线程之间进行同步。出于性能原因,最好使线程尽可能独立。我相信我确实需要它,实际上您可能不需要。

要点3。。您可以使用简单的锁。我从https://codereview.stackexchange.com/questions/160277/implementing-a-thread-safe-lrucache中汲取了灵感,并提出了以下建议,我认为这些建议可以安全地用于我的目的

import threading

stubs = {}
lock = threading.Lock()


def maybe_new_stub(host):
    """ returns stub from cache and populates the stubs cache if new is created """
    with lock:
        if host not in stubs:
            channel = grpc.insecure_channel('%s:6666' % host)
            stub = cli_pb2_grpc.BrkStub(channel)
            stubs[host] = stub
        return stubs[host]

要点4。最好使用现有的库。我还没有准备好要担保的东西。

答案 5 :(得分:0)

我不确定这些答案中的任何一个都能满足您的要求。

我有一个类似的问题,我正在使用名为cachetools的lrucache的替代品,它允许您传递锁以使其更安全。