threadlocals有什么问题?

22 投票
5 回答
5492 浏览
提问于 2025-04-15 17:07

在Django的世界里,大家似乎都不喜欢线程本地存储(threadlocals)。我看了Armin写的文章,里面提到线程本地存储不好,主要是因为它不够优雅。

不过我有一个情况,使用线程本地存储会让事情变得简单很多。我有一个应用,用户会有子域名,所以所有的模型都需要访问当前的子域名。如果只是因为线程本地存储不够优雅,或者会导致代码不够稳健,那从请求中传递这些信息就显得不太划算。

而且很多Java框架似乎都在大量使用线程本地存储,那它们的情况和Python/Django的有什么不同呢?

5 个回答

3

很多Java框架似乎都在频繁使用线程局部变量,那它们的情况和Python/Django有什么不同呢?

CPython的解释器有一个叫做全局解释器锁(GIL)的东西,这意味着在任何时候,解释器只能执行一个Python线程。虽然我不太明白为什么Python的解释器一定需要使用多个操作系统线程来实现这一点,但实际上CPython确实是这样做的。

Java的主要锁机制是通过对象的监视器锁来实现的。这是一种去中心化的方法,允许在多核或多处理器的CPU上使用多个并发线程,但这也给程序员带来了更复杂的同步问题。

这些同步问题只会出现在“共享可变状态”中。如果状态是不可变的,或者像线程局部变量那样不被共享,那么对于Java程序员来说,这就少了一个复杂的问题。

CPython程序员仍然需要处理竞争条件的可能性,但一些更复杂的Java问题(比如发布问题)可能是由解释器来解决的。

CPython程序员还可以选择用Python可以调用的C或C++代码来编写性能关键的代码,这样就不受GIL的限制。技术上,Java程序员也可以通过JNI实现类似的功能,但这在Java中被认为不如在Python中可接受。

22

我不太喜欢使用线程本地变量(threadlocals),因为这样会引入一种隐含的非本地耦合。其实,我经常在各种非HTTP的场景中使用模型,比如本地管理命令、数据导入导出等等。如果我在models.py中访问了一些线程本地变量的数据,那我就得想办法确保每次使用模型的时候这些数据都是准备好的,这样可能会变得相当麻烦。

在我看来,更明确的代码更干净,也更容易维护。如果一个模型的方法需要一个子域名才能正常工作,那么这个要求应该很明显,方法应该把这个子域名作为参数接受。

如果我实在找不到其他方法来存储请求数据在线程本地变量中,我至少会在一个单独的模块中实现一些包装方法,这些方法访问线程本地变量,并用需要的数据调用模型的方法。这样一来,models.py就可以保持独立,模型也可以在没有线程本地变量耦合的情况下使用。

18

我觉得使用线程本地变量(threadlocals)没有什么问题。没错,它算是一种全局变量,但除此之外,它就是一个普通的工具。我们就是为了这个目的来用它的(在当前请求的上下文中存储子域模型),而且它工作得非常好。

所以我想说,做事情要用对工具。在这种情况下,线程本地变量让你的应用看起来更简洁,而不是在所有模型方法中到处传递子域模型(更别提有时候根本不可能做到这一点——比如当你在重写Django的管理器方法来根据子域限制查询时,你根本无法传递额外的东西给get_query_set,举个例子——所以线程本地变量就是最自然也是唯一的解决方案)。

撰写回答