本篇所有操作均在基于 Ubuntu 16.04 LTS 的虚拟机下完成,且使用 Vagrant 来操作虚拟机系统,虚拟机系统 VirtualBox Version: 7.0
一、本地线程
环境准备:
Python 2.7.11+
pip==9.0.3
flask==0.11.1
httpie==0.9.4
werkzeug==0.11.10
1、threading.local
本地线程(Thread Local)希望不同的线程对于内容的修改只在线程内发挥作用,线程之间互不影响。简单来说,我们定义并启动了多个线程来执行任务,但是每个线程只能执行自己线程内部的代码块,不去影响其他线程,为了获取线程执行的进度/结果,需要定义一个 threading.local() 对象来保存这些线程的状态。如下代码所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# coding=utf-8
import threading
mydata = threading.local()
mydata.number = 42
print mydata.number
log = []
def f1():
mydata.number = 11
log.append(mydata.number)
def f2():
mydata.number = 12
log.append(mydata.number)
def f3():
mydata.number = 13
log.append(mydata.number)
"""
定义三个线程 thread1、thread2和thread3 并启动
"""
thread1 = threading.Thread(target=f1)
thread2 = threading.Thread(target=f2)
thread3 = threading.Thread(target=f3)
thread1.start()
thread1.join()
thread2.start()
thread2.join()
thread3.start()
thread3.join()
print log
print mydata.number
执行结果如下:
1
2
3
42
[11, 12, 13] # 在线程内变成了 mydata.number 的值
42 # 但是没有影响到开始设置的值
本地线程的原理就是:在 threading.current_thread().__dict__
里添加一个包含对象 mydata 的 id 值的 key,用来保存不同线程的状态。
2、werkzeug.local.Local
Werkzeug 自己实现了本地线程。werkzeug.local.Local 和 上述的 threading.local 的区别如下:
- Werkzeug 使用了自定义的
__storage__
保存不同线程下的状态 - Werkzeug 提供了释放本地线程的 release_local 方法
- Werkzeug 使用如下方法得到 get_ident 函数,用来获得线程/协程标识符
1 2 3 4 5 6 7
try: from greenlet import getcurrent as get_ident except ImportError: try: from thread omport get_ident except ImportError: from _thread import get_ident
以上代码块表示如果已经安装了 Greentlet 第三方包,就会优先选择 Greentlet,否则使用系统线程。
Greentlet 是以 C 扩展模块形式接入 Python 的轻量级协程,它运行在操作系统进程的内部,但是会被协作式地调度。
Werkzeug 还实现了两种数据结构:
- LocalStack: 基于 werkzeug.local.Local 实现的栈结构,可以将对象推入、弹出,也可以快速拿到栈顶对象。
- LocalProxy: 作用和名字一样,是标准的代理模式。构造此结构时接受一个可以调用的参数(一般是函数),这个函数执行后就是通过 LocalStack 实例化的栈的栈顶对象。对于 LocalProxy 对象的操作实际上都会转发到这个栈顶对象(也就是一个 Thread Local 对象)上面。