Python的“全局”变量是线程局部的吗?
我想在内存中使用一个线程本地的缓存,来存储一个数据库中的值,这个值在请求和响应的过程中不会改变,但可能会被调用几百次(甚至几千次)。我了解到,使用一个“全局”的模块变量是一种实现这种缓存的方法。
例如:
#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 个回答
7
不,访问全局变量不是线程安全的。线程之间并不会各自拥有全局变量的副本,全局变量是线程共享的。
这段代码:
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
之后,另一个线程可能会对它进行修改。
4
不,你的理解有两个地方是错的。
首先,提到“线程”这个词有点模糊。根据服务器的配置,Django可以通过线程、进程,或者两者结合的方式来运行(想了解更多可以查看mod_wsgi的文档)。如果每个进程只有一个线程,那么可以保证每个进程中只有一个模块实例可用。但这完全取决于具体的配置。
即便如此,也不能说每次请求/响应的循环中“恰好会有一次”对那个函数的调用。这是因为一个进程的生命周期和这个循环是完全无关的。一个进程会处理多个请求,所以那个变量会在所有这些请求中保持不变。