2017年8月

引子

以前对于函数作用域和栈的关系非常不清晰,只知道栈是系统为一个程序预分配的一块空间,程序的预声明变量都存在系统的栈当中,用malloc*分配的空间则在堆当中。

“作用域”和“栈”是一个抽象的概念,并没有深入的理解他们之间的联系。

一直以来都对代码如何在计算机中运行这一块饶有兴趣,最近刚好有时间来了解一下这里面的原理。

- 阅读剩余部分 -

CmdTree是一个命令行库,参见 这里

https://github.com/winkidney/cmdtree

轮子满地都是……然而我又再造了一个(自尽

现有的库虽然有各自的设计哲学,却无法满足某些特定的需求:)。

能不能享受click带来的便利的同时,也能获取到argparse的子命令支持呢?

为了不再每次都要重复解决这个问题,复制粘贴代码,我写了CmdTree.

- 阅读剩余部分 -

引子

上周基友忽然问我,周六能不能帮他写写代码,说是参加什么比赛,可以多个人一起做题,赛程好像是24小时。

当时我感觉应该是比较麻烦的事情,准备不做的,不过后面一想,万一很有趣呢,哈哈。

到了周六,都差点忘了有这回事,被一个QQ消息提醒了,就开始做题了。后面才知道,这个活动叫“极限编程大赛”,会临场发布一些网上基本上搜不到的问题,给你解决,分数根据跑过的测试用例来计算。

我帮他做了两个题,都还算比较理想,也花了不少时间,但感觉颇有收获:)

- 阅读剩余部分 -

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返回执行完毕之后被销毁。