转自:http://www.cnblogs.com/holbrook/archive/2012/03/01/2376408.html
前面介绍过多线程的基本概念,理解了这些基本概念,掌握python多线程编程就比较容易了。
在开始之前,首先要了解一下python对多线程的支持。
Python虚拟机使用GIL(Global Interpreter Lock,全局解释器锁)来互斥线程对共享资源的访问,暂时无法利用多处理器的优势。
在语言层面,Python对多线程提供了很好的支持,Python中多线程相关的模块包括:thread,threading,Queue。可以方便地支持创建线程、互斥锁、信号量、同步等特性。
thread:多线程的底层支持模块,一般不建议使用。
threading:对thread进行了封装,将一些线程的操作对象化,提供下列类:
Thread 线程类
Timer与Thread类似,但要等待一段时间后才开始运行
Lock 锁原语
RLock 可重入锁。使单线程可以再次获得已经获得的锁
Condition 条件变量,能让一个线程停下来,等待其他线程满足某个“条件”
Event 通用的条件变量。多个线程可以等待某个事件发生,在事件发生后,所有的线程都被激活
Semaphore为等待锁的线程提供一个类似“等候室”的结构
BoundedSemaphore 与semaphore类似,但不允许超过初始值
Queue:实现了多生产者(Producer)、多消费者(Consumer)的队列,支持锁原语,能够在多个线程之间提供很好的同步支持。提供的类:
Queue队列
LifoQueue后入先出(LIFO)队列
PriorityQueue 优先队列
其中Thread类是你主要的线程类,可以创建进程实例。该类提供的函数包括:
getName(self) 返回线程的名字
isAlive(self) 布尔标志,表示这个线程是否还在运行中
isDaemon(self) 返回线程的daemon标志
join(self, timeout=None) 程序挂起,直到线程结束,如果给出timeout,则最多阻塞timeout秒
run(self) 定义线程的功能函数
setDaemon(self, daemonic) 把线程的daemon标志设为daemonic
setName(self, name) 设置线程的名字
start(self) 开始线程执行
如果你特别在意性能,还可以考虑一些“微线程”的实现:
Stackless Python:Python的一个增强版本,提供了对微线程的支持。微线程是轻量级的线程,在多个线程间切换所需的时间更多,占用资源也更少。
greenlet:是 Stackless 的副产品,其将微线程称为 “tasklet” 。tasklet运行在伪并发中,使用channel进行同步数据交换。而”greenlet”是更加原始的微线程的概念,没有调度。你可以自己构造微线程的调度器,也可以使用greenlet实现高级的控制流。
# encoding: UTF-8
import threading
import time
class MyThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "I'm "+self.name+' @ '+str(i)
print msg
def test():
for i in range(5):
t = MyThread()
t.start()
if __name__ == '__main__':
test()
执行结果:
从代码和执行结果我们可以看出,多线程程序的执行顺序是不确定的。当执行到sleep语句时,线程将被阻塞(Blocked),到sleep结束后,线程进入就绪(Runnable)状态,等待调度。而线程调度将自行选择一个线程执行。上面的代码中只能保证每个线程都运行完整个run函数,但是线程的启动顺序、run函数中每次循环的执行顺序都不能确定。I'm Thread-1 @ 0
I'm Thread-2 @ 0
I'm Thread-5 @ 0
I'm Thread-3 @ 0
I'm Thread-4 @ 0
I'm Thread-3 @ 1
I'm Thread-4 @ 1
I'm Thread-5 @ 1
I'm Thread-1 @ 1
I'm Thread-2 @ 1
I'm Thread-4 @ 2
I'm Thread-5 @ 2
I'm Thread-2 @ 2
I'm Thread-1 @ 2
I'm Thread-3 @ 2
# encoding: UTF-8
import threading
import time
class MyThread(threading.Thread):
def run(self):
global num
time.sleep(1)
num = num+1
msg = self.name+' set num to '+str(num)
print msg
num = 0
def test():
for i in range(5):
t = MyThread()
t.start()
if __name__ == '__main__':
test()
但是运行结果是不正确的:
>>> Thread-5 set num to 3
Thread-4 set num to 5
Thread-2 set num to 1
Thread-3 set num to 4
Thread-1 set num to 2
问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。
上面的例子引出了多线程编程的最常见问题:数据共享。当多个线程都修改某一个共享数据的时候,需要进行同步控制。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
threading模块中定义了Lock类,可以方便的处理锁定:
#创建锁
mutex = threading.Lock()
#锁定
mutex.acquire([timeout])
#释放
mutex.release()
其中,锁定方法acquire可以有一个超时时间的可选参数timeout。如果设定了timeout,则在超时后通过返回值可以判断是否得到了锁,从而可以进行一些其他的处理。
使用互斥锁实现上面的例子的代码如下:
# encoding: UTF-8
import threading
import time
class MyThread(threading.Thread):
def run(self):
global num
time.sleep(1)
if mutex.acquire(1):
num = num+1
msg = self.name+' set num to '+str(num)
print msg
mutex.release()
num = 0
mutex=threading.Lock()
def test():
for i in range(5):
t = MyThread()
t.start()
if __name__ == '__main__':
test()
>>> Thread-1 set num to 1
Thread-2 set num to 2
Thread-4 set num to 3
Thread-3 set num to 4
Thread-5 set num to 5