I'd like to use an in-memory thread-local cache for a value from the database that isn't going to change during a request/response cycle, but gets called hundreds (potentially thousands) of times. My limited understanding is that using a "global"/module variable is one way to implement this type of cache.
我想使用内存中的线程本地缓存来获取数据库中的值,该值在请求/响应周期中不会发生变化,但会被调用数百次(可能数千次)。我有限的理解是使用“全局”/模块变量是实现此类缓存的一种方法。
e.g.:
#somefile.py
foo = None
def get_foo(request):
global foo
if not foo:
foo = get_foo_from_db(request.blah)
return foo
I'm wondering whether using this type of "global" is thread-safe in python, and that therefore I can be comfortable that get_foo_from_db() will get called exactly once per request/response cycle in django (using either runserver or gunicorn+gevent). Is my understanding correct? This thing gets called enough that even using memcached to store the value is going to be a bottleneck (I'm profiling it as we speak).
我想知道在python中使用这种类型的“全局”是否是线程安全的,因此我很乐意在django中每个请求/响应周期调用get_foo_from_db()一次(使用runserver或gunicorn + gevent) )。我的理解是否正确?这个东西被充分调用,即使使用memcached来存储该值也将成为一个瓶颈(我在说话时对它进行分析)。
2 个解决方案
#1
3
No, you are wrong on two counts.
不,你错了两点。
Firstly, the use of "threads" is a bit vague here. Depending on how its server is configured, Django can be served either using threads or processes or both (see the mod_wsgi documentation for a full discussion). If there is a single thread per process, then you can can guarantee that only one instance of a module will be available to each process. But that is highly dependent on that configuration.
首先,这里使用“线程”有点模糊。根据服务器的配置方式,可以使用线程或进程或两者来提供Django(有关完整讨论,请参阅mod_wsgi文档)。如果每个进程只有一个线程,那么您可以保证每个进程只能使用一个模块实例。但这在很大程度上取决于该配置。
Even so, it is still not the case that there will be "exactly one" call to that function per request/response cycle. This is because the lifetime of a process is entirely unrelated to that cycle. A process will last for multiple requests, so that variable will persist for all of those requests.
即便如此,仍然不是每个请求/响应周期对该功能进行“完全一次”调用的情况。这是因为过程的生命周期与该循环完全无关。进程将持续多个请求,因此变量将持续存在于所有这些请求中。
#2
3
No, access to globals is not thread-safe. Threads do not get their own copy of globals, globals are shared among threads.
不,访问全局变量不是线程安全的。线程没有得到自己的全局变量,全局变量在线程之间共享。
The code:
if not foo:
foo = get_foo_from_db(request.blah)
compiles to several python bytecode statements:
编译成几个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)
A thread switch can occur after each and every bytecode execution, so another thread could alter foo
after you tested it.
每次执行字节码后都会发生线程切换,因此在测试之后,另一个线程可能会改变foo。
#1
3
No, you are wrong on two counts.
不,你错了两点。
Firstly, the use of "threads" is a bit vague here. Depending on how its server is configured, Django can be served either using threads or processes or both (see the mod_wsgi documentation for a full discussion). If there is a single thread per process, then you can can guarantee that only one instance of a module will be available to each process. But that is highly dependent on that configuration.
首先,这里使用“线程”有点模糊。根据服务器的配置方式,可以使用线程或进程或两者来提供Django(有关完整讨论,请参阅mod_wsgi文档)。如果每个进程只有一个线程,那么您可以保证每个进程只能使用一个模块实例。但这在很大程度上取决于该配置。
Even so, it is still not the case that there will be "exactly one" call to that function per request/response cycle. This is because the lifetime of a process is entirely unrelated to that cycle. A process will last for multiple requests, so that variable will persist for all of those requests.
即便如此,仍然不是每个请求/响应周期对该功能进行“完全一次”调用的情况。这是因为过程的生命周期与该循环完全无关。进程将持续多个请求,因此变量将持续存在于所有这些请求中。
#2
3
No, access to globals is not thread-safe. Threads do not get their own copy of globals, globals are shared among threads.
不,访问全局变量不是线程安全的。线程没有得到自己的全局变量,全局变量在线程之间共享。
The code:
if not foo:
foo = get_foo_from_db(request.blah)
compiles to several python bytecode statements:
编译成几个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)
A thread switch can occur after each and every bytecode execution, so another thread could alter foo
after you tested it.
每次执行字节码后都会发生线程切换,因此在测试之后,另一个线程可能会改变foo。