22

Django 世界中的每个人似乎都讨厌 threadlocals http://code.djangoproject.com/ticket/4280,http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser)。我阅读了 Armin 关于此的文章(http://lucumr.pocoo.org/2006/7/10/why-i-cant-stand-threadlocal-and-others),但其中大部分取决于 threadlocals 是不好的,因为它是不雅。

我有一个场景,theadlocals 会让事情变得更容易。(我有一个应用程序,人们将拥有子域,因此所有模型都需要访问当前子域,并且从请求中传递它们是不值得的,如果 threadlocals 的唯一问题是它们不优雅,或者变得脆弱代码。)

还有很多 Java 框架似乎大量使用 threadlocals,那么它们的情况与 Python/Django 的情况有何不同?

4

5 回答 5

22

我避免使用这种线程局部变量,因为它引入了隐式的非局部耦合。我经常以各种非 HTTP 导向的方式(本地管理命令、数据导入/导出等)使用模型。如果我访问models.py 中的一些threadlocals 数据,现在我必须找到某种方法来确保在我使用我的模型时始终填充它,这可能会变得非常难看。

在我看来,更明确的代码更简洁、更易于维护。如果模型方法需要一个子域才能运行,那么通过让该方法接受该子域作为参数,这一事实应该很明显。

如果我绝对找不到将请求数据存储在 threadlocals 中的方法,我至少会在一个单独的模块中实现包装器方法,该模块访问 threadlocals 并使用所需数据调用模型方法。这样,models.py 仍然是独立的,并且可以在没有 threadlocals 耦合的情况下使用模型。

于 2009-12-17T20:53:51.110 回答
18

我不认为 threadlocals 有什么问题——是的,它是一个全局变量,但除此之外它是一个普通的工具。我们仅将它用于此目的(将子域模型存储在来自中间件的当前请求的全局上下文中)并且它工作得很好。

所以我说,使用正确的工具来完成这项工作,在这种情况下,threadlocals 使您的应用程序比在所有模型方法中传递子域模型更加优雅(更不用说它甚至不总是可能的事实 - 当您覆盖 django 时管理器方法来限制子域的查询,例如,您无法将任何额外的东西传递给 get_query_set - 所以 threadlocals 是自然且唯一的答案)。

于 2009-12-17T11:39:39.747 回答
3

还有很多 Java 框架似乎大量使用 threadlocals,那么它们的情况与 Python/Django 的情况有何不同?

CPython 的解释器有一个全局解释器锁 (GIL),这意味着解释器在任何给定时间只能执行一个 Python 线程。我不清楚 Python 解释器实现是否必然需要使用多个操作系统线程来实现这一点,尽管实际上 CPython 确实如此。

Java 的主要锁定机制是通过对象的监视器锁定。这是一种分散的方法,允许在多核和/或多处理器 CPU 上使用多个并发线程,但也会产生更复杂的同步问题供程序员处理。

这些同步问题仅出现在“共享可变状态”中。如果状态不是可变的,或者在 ThreadLocal 的情况下它不是共享的,那么对于 Java 程序员来说,这是一个不太复杂的问题。

CPython 程序员仍然必须处理可能出现的竞态条件,但一些更深奥的 Java 问题(例如发布)可能由解释器解决。

CPython 程序员还可以选择在不适用 GIL 限制的 Python 可调用 C 或 C++ 代码中编写性能关键代码。从技术上讲,Java 程序员通过 JNI 也有类似的选择,但是这在 Java 中被正确或错误地认为比在 Python 中更不可接受。

于 2009-12-17T11:05:56.367 回答
2

当您使用多个线程并希望将某些对象本地化到特定线程时,您想使用 threadlocals,例如。每个线程都有一个数据库连接。在您的情况下,您希望更多地将其用作全局上下文(如果我理解正确的话),这可能是一个坏主意。它会使你的应用程序变得更慢、更耦合并且更难测试。

为什么从请求中传递它不值得?为什么不将其存储在会话或用户配置文件中?

与 Java 的不同之处在于,Web 开发比 Python/PERL/PHP/Ruby 世界中的状态要多得多,因此人们习惯于各种上下文和类似的东西。我不认为这是一个优势,但一开始似乎确实如此。

于 2009-12-17T09:24:53.353 回答
0

我发现使用 ThreadLocal 是在 HTTP 请求/响应环境(即任何 webapp)中实现依赖注入的绝佳方式。您只需设置一个 servlet 过滤器,以便在接收请求时将所需的对象“注入”到线程中,并在返回响应时将其“取消注入”。

这是一个聪明人的 DI,没有所有的 XML 丑陋,没有 Spring Jars 的 MB(更不用说它的学习曲线),也没有所有神秘的重复 @annotation 废话,因为它没有单独注入许多具有依赖关系的对象实例,它可能快得多,使用更少的内存。

它运行得非常好,我们开源了 exPOJO 过滤器,它可以使用 ThreadLocal 注入 Hibernate 会话或 JDO PersistenceManager:

http://www.expojo.com

于 2012-01-22T11:44:12.737 回答