我想使用内存中的线程本地缓存来获取数据库中的值,该值在请求/响应周期中不会发生变化,但会被调用数百次(可能数千次)。我有限的理解是使用“全局”/模块变量是实现此类缓存的一种方法。
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存储该值也将成为一个瓶颈(我在说话时对其进行分析)。
答案 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)。如果每个进程都有一个线程,那么您可以保证每个进程只能使用一个模块实例。但这在很大程度上取决于该配置。
即便如此,仍然不是每个请求/响应周期对该功能进行“完全一次”调用的情况。这是因为进程的生命周期完全不相关到该循环。一个进程将持续多个请求,因此变量将持续存在于所有这些请求中。