7

我想对数据库中的一个值使用内存中的线程本地缓存,该值在请求/响应周期内不会更改,但会被调用数百次(可能是数千次)。我有限的理解是,使用“全局”/模块变量是实现这种缓存的一种方法。

例如:

#somefile.py

foo = None

def get_foo(request):
  global foo
  if not foo:
    foo = get_foo_from_db(request.blah)
  return foo

我想知道在 python 中使用这种类型的“全局”是否是线程安全的,因此我可以放心 get_foo_from_db() 在 django 中每个请求/响应周期都会被调用一次(使用 runserver 或 gunicorn+gevent )。我的理解正确吗?这个东西被调用得足够多,即使使用 memcached 来存储值也会成为一个瓶颈(我正在分析它)。

4

2 回答 2

6

不,对全局变量的访问不是线程安全的。线程没有自己的全局变量副本,全局变量在线程之间共享。

编码:

if not foo:
    foo = get_foo_from_db(request.blah)

编译成几个 python 字节码语句:

  2           0 LOAD_FAST                1 (foo)
              3 POP_JUMP_IF_TRUE        24

  3           6 LOAD_GLOBAL              0 (get_foo_from_db)
              9 LOAD_FAST                0 (request)
             12 LOAD_ATTR                1 (blah)
             15 CALL_FUNCTION            1
             18 STORE_FAST               1 (foo)
             21 JUMP_FORWARD             0 (to 24)

每次执行字节码后都可能发生线程切换,因此foo 在您测试后另一个线程可能会发生变化。

于 2013-03-12T15:39:47.003 回答
3

不,你在两个方面是错误的。

首先,“线程”的使用在这里有点模糊。根据其服务器的配置方式,可以使用线程或进程或同时使用两者来为 Django 提供服务(请参阅mod_wsgi 文档以获取完整讨论)。如果每个进程只有一个线程,那么您可以保证每个进程只有一个模块实例可用。但这高度依赖于该配置。

即便如此,每个请求/响应周期都不会“完全”调用该函数。这是因为进程的生命周期与该周期完全无关。一个进程将持续多个请求,因此该变量将针对所有这些请求持续存在。

于 2013-03-12T15:42:11.473 回答