理解 WERKZEUG 的 THREADLOCAL 实现
https://github.com/pallets/werkzeug
文件在 werkzeug/local.py
看这部分源码,主要想搞清楚以下几个问题:
- ThreadLocal解决什么问题
- ThreadLocal如何实现
- ThreadLocal的生命周期管理
ThreadLocal解决什么问题
ThreadLocal是需要拿来和全局变量对比的。
当大家都需要用相同的逻辑,引用相同的变量名/资源来完成自己的逻辑,但又不希望不同的线程直接一个全局引用会对别的线程造成影响,需要使用ThreadLocal来解决这个问题。
ThreadLocal 解决的不是多线程编程资源共享的问题,更多的是在逻辑层面,用来管理一些跟随线程生命周期的上下文数据,让程序逻辑更加容易编写和维护。
所有的代码能公用同一个逻辑,而不会对另外的线程(全局资源)造成影响。
ThreadLocal如何实现
每一个运行中的Thread都会有自己的一个标识符,这个标识符是线程数据结构的一部分。
通过如下步骤,可以实现ThreadLocal
一个进程内全局的Storage,依靠一个Map来存储ThreadID和其对应的数据。
接下来,使用Local()来获取本地变量,实际上是一个查询函数用于获取数据,这个函数帮忙做的事情,就是自动获取Thread ID,从全局的ThreadVariableMap中获取到线程对应的数据集合。
如此一来,每个线程,都可以使用相同的代码逻辑来执行逻辑而不会对全局资源产生影响/依赖。
在Werkzeug中的ThreadLocal大概是这样的结构。
+---------------------------------------------------------+
| |
| +----------------------------------+ |
| | | |
| | LocalStack/LocalsRegistry | |
| | | |
| +---------^------------------------+ |
| | |
| | |
| | |
| | |
| | |
| +---------------+-------+ |
| | | |
| | Local Variables | |
| | Marked by Thred-ID | |
| | or Greenlet-ID | |
| | | |
| +-----------------------+ |
| |
+---------------------------^-----------------------------+
|
|
|
|
| Identifier(Greenet-ID or
| Thread-ID)
|
+---------------------------+-----------------------------+
| |
| LocalProxy |
| |
+---------------------------^-----------------------------+
|
|
|
+---------------------------+-----------------------------+
| |
| Application |
| |
+---------------------------------------------------------+
ThreadLocal的生命周期管理
ThreadLocal是的生命周期是跟随Thread的生命周期的。
通常来说,ThreadLocal应该在Thread生命周期结束的时候进行销毁和清理,不然就会造成内存泄漏。
在Werkeug中,ThreadLocal用于WSGIRequest和WSGIResponse,可以看到,ThreadLocal被作为一个Middleware被插入WSGI处理流程中,然后在Response返回执行完毕之后被销毁。