threading 模块
threading 模块是python多线程编程提供的更高级别的模块,它不仅提供了 Thread 类,还提供了各种非常好用的同步机制,下表列出了 threading 模块里所有的对象。
threading 模块对象 | 说明 |
---|---|
Thread | 表示一个线程的执行的对象 |
Lock | 锁原语对象(跟 thread 模块里的锁对象相同) |
RLock | 可重入锁对象。使单线程可以再次获得已经获得了的锁(递归锁定)。 |
Condition | 条件变量对象能让一个线程停下来, 等待其它线程满足了某个“条件”。如,状态的改变或值的改变。 |
Event | 通用的条件变量。多个线程可以等待某个事件的发生,在事件发生后,所有的线程都会被激活。 |
Semaphore | 为等待锁的线程提供一个类似“等候室”的结构 |
BoundedSemaphore | 与 Semaphore 类似,只是它不允许超过初始值 |
Timer | 与 Thread 相似,只是,它要等待一段时间后才开始运行。 |
除了各种同步对象和线程对象外,threading 模块还提供了下列函数:
threading 模块函数 | 说明 |
---|---|
activeCount() | 当前活动的线程对象的数量 |
currentThread() | 返回当前线程对象 |
enumerate() | 返回当前活动线程的列表 |
settrace(func) | 为所有线程设置一个跟踪函数 |
setprofile(func) | 为所有线程设置一个 profile 函数 |
Thread 类
threading 的 Thread 类是你主要的运行对象。它有很多 thread 模块里没有的函数,详见下表:
函数 | 说明 |
---|---|
start() | 开始线程的执行 |
run() | 定义线程的功能的函数(一般会被子类重写) |
join(timeout=None) | 程序挂起,直到线程结束;如果给了 timeout,则最多阻塞 timeout 秒 |
getName() | 返回线程的名字 |
setName(name) | 设置线程的名字 |
isAlive() | 布尔标志,表示这个线程是否还在运行中 |
isDaemon() | 返回线程的 daemon 标志 |
setDaemon(daemonic) | 把线程的 daemon 标志设为 daemonic (一定要在调用 start()函数前调用) |
使用 Thread 类,可以有多种方法来创建线程。这里介绍三种方法,下面我们将用这三种方法分别重写之前的例子。
创建一个 Thread 的实例,传给它一个函数
#!/usr/bin/env python
import threading
from time import sleep, ctime
loops = [ 4, 2 ]
def loop(nloop, nsec):
print('start loop', nloop, 'at:', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime())
def main():
print('starting at:', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = threading.Thread(target=loop,
args=(i, loops[i]))
threads.append(t)
for i in nloops: # start threads
threads[i].start()
for i in nloops: # wait for all
threads[i].join() # threads to finish
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
运行结果:
starting at: Tue Aug 22 21:36:09 2017
start loop 0 at: Tue Aug 22 21:36:09 2017
start loop 1 at: Tue Aug 22 21:36:09 2017
loop 1 done at: Tue Aug 22 21:36:11 2017
loop 0 done at: Tue Aug 22 21:36:13 2017
all DONE at: Tue Aug 22 21:36:13 2017
在实例化每个 Thread 对象的时候, 我们把函数(target) 和参数(args) 传进去, 得到返回的 Thread实例。实例化一个 Thread(调用Thread())与调用 thread.start_new_thread()之间最大的区别就是,新的线程不会立即开始。在你创建线程对象,但不想马上开始运行线程的时候,这是一个很有用的同步特性。所有的线程都创建了之后,再一起调用 start()函数启动,而不是创建一个启动一个。而且不用再管理一堆锁(分配锁, 获得锁, 释放锁, 检查锁的状态等), 只要简单地对每个线程调用 join()函数就可以了。join()会等到线程结束,或者在指定timeout 参数的时候,等到超时为止。join()的另一个比较重要的方面是它可以完全不用调用。一旦线程启动后,就会一直运行,直到线程的函数结束,退出为止。如果你的主线程除了等线程结束外,还有其它的事情要做(如处理或等待其它的客户请求), 那就不用调用 join(), 只有在你要等待线程结束的时候调用 join()即可。
创建一个 Thread 的实例,传给它一个可调用的类对象
与传一个函数很相似的另一个方法是在创建线程的时候,传一个可调用的类的实例供线程启动的时候执行——这是多线程编程的一个更为面向对象的方法。相对于一个或几个函数来说,由于类对象里可以使用类的强大的功能,可以保存更多的信息,这种方法更为灵活。
#!/usr/bin/env python
import threading
from time import sleep, ctime
loops = [ 4, 2 ]
class ThreadFunc(object):
def __init__(self, func, args, name=''):
self.name = name
self.func = func
self.args = args
def __call__(self):
self.func(*self.args)
def loop(nloop, nsec):
print('start loop', nloop, 'at:', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime())
def main():
print('starting at:', ctime())
threads = []
nloops = range(len(loops))
for i in nloops: # create all threads
t = threading.Thread(
target=ThreadFunc(loop, (i, loops[i]),
name=loop.__name__))
threads.append(t)
for i in nloops: # start all threads
threads[i].start()
for i in nloops: # wait for completion
threads[i].join()
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
运行结果:
starting at: Tue Aug 22 22:09:04 2017
start loop 0 at: Tue Aug 22 22:09:04 2017
start loop 1 at: Tue Aug 22 22:09:04 2017
loop 1 done at: Tue Aug 22 22:09:06 2017
loop 0 done at: Tue Aug 22 22:09:08 2017
all DONE at: Tue Aug 22 22:09:08 2017
这次又改了些什么呢?主要是增加了 ThreadFunc 类和创建 Thread 对象时会实例化一个可调用类 ThreadFunc 的类对象。也就是说,我们实例化了两个对象。下面,来仔细地看一看 ThreadFunc类吧。我们想让这个类在调用什么函数方面尽量地通用,并不局限于那个 loop()函数。所以,我们加了一些修改,如,这个类保存了函数的参数,函数本身以及函数的名字字符串。构造函数init()里做了这些值的赋值工作。创建新线程的时候, Thread 对象会调用我们的 ThreadFunc 对象, 这时会用到一个特殊函数call()。
从 Thread 派生出一个子类,创建一个这个子类的实例
#!/usr/bin/env python
import threading
from time import sleep, ctime
loops = [ 4, 2 ]
class MyThread(threading.Thread):
def __init__(self, func, args, name=''):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args
def run(self):
self.func(*self.args)
def loop(nloop, nsec):
print('start loop', nloop, 'at:', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime())
def main():
print('starting at:', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop, (i, loops[i]),
loop.__name__)
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
运行结果:
starting at: Tue Aug 22 22:08:03 2017
start loop 0 at: Tue Aug 22 22:08:03 2017
start loop 1 at: Tue Aug 22 22:08:03 2017
loop 1 done at: Tue Aug 22 22:08:05 2017
loop 0 done at: Tue Aug 22 22:08:07 2017
all DONE at: Tue Aug 22 22:08:07 2017
一般来说,从面向对象的角度讲,建议使用第三种方法,第三种方法扩展性更好。