python“全局”(模块)变量是否为本地线程?

时间:2013-03-12 15:37:47

标签: python django

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

e.g:

#somefile.py

foo = None

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

我想知道在python中使用这种类型的“全局”是否是线程安全的,因此我很乐意在django中每个请求/响应周期调用get_foo_from_db()一次(使用runserver或者gunicorn + GEVENT)。我的理解是否正确?这个东西被充分调用,即使使用memcached存储该值也将成为一个瓶颈(我在说话时对其进行分析)。

2 个答案:

答案 0 :(得分:4)

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

代码:

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

答案 1 :(得分:3)

不,你有两个错误。

首先,这里使用“线程”有点模糊。根据服务器的配置方式,可以使用线程或进程或两者来提供Django(有关完整讨论,请参阅mod_wsgi documentation)。如果每个进程都有一个线程,那么您可以保证每个进程只能使用一个模块实例。但这在很大程度上取决于该配置。

即便如此,仍然不是每个请求/响应周期对该功能进行“完全一次”调用的情况。这是因为进程的生命周期完全不相关到该循环。一个进程将持续多个请求,因此变量将持续存在于所有这些请求中。