2.5 Python常用类库中的threading
除了前面介绍的本书必须使用的两个类库ThensorFlow与OpenCV,Python还提供了多种多样的用于不同目的和方向的类库。
Python常用类库参见表2-1。
表2-1 Python常用类库
表2-1给出了Python中常用类库的名称和说明,到目前为止,Python中已经有7000多个可以使用的类库可供计算机工程人员以及科学研究人员使用。
2.5.1 threading库的使用
对于希望充分利用计算机性能的程序设计者来说,多线程的应用是必不可少的一个重要技能。多线程类似于使用计算机的一个核心执行多个不同任务。多线程的好处如下:
- 使用线程可以把需要使用大量时间的计算任务放到后台去处理。
- 减少资源占用,加快程序的运行速度。
- 在传统的输入输出以及网络收发等普通操作上,后台处理可以美化当前界面,增加界面的人性化。
本节将详细介绍Python中操作线程的模块:threading,相对于Python既有的多线程模块thread,threading重写了部分API模块,对thread进行了二次封装,从而大大提高了执行效率。
2.5.2 threading模块中最重要的Thread类
Thread是threading模块中最重要的类之一,可以使用它来创造线程。其具体使用方法是创建一个threading.Thread对象,在它的初始化函数中将需要调用的对象作为初始化参数传入,具体代码如程序2-3所示。
【程序2-3】
在上面定义的MyThread类中,重写了从父对象继承的run方法,run方法中,将一个全局变量逐一增加,在接下来的代码中,创建了5个独立的对象,分别调用其start方法,最后将结果逐一打印。
可以看到在程序中,每个线程被赋予了一个名字,然后设置每隔0.3秒打印输出本线程的计数,即计数加1。而count被人为地设置成全局共享变量,因此在每个线程中都可以自由地对其进行访问。
程序运行结果如图2-29所示。
图2-29 程序运行结果
通过上面的结果可以看到,每个线程被起了一个对应的名字,而在运行的时候,线程所计算的计数被同时增加,这样可以证明,在程序运行过程中,2个线程同时对一个数进行操作,并将其结果进行打印。
其中的run方法和start方法并不是threading自带的方法,而是从Python本身的线程处理模块Thread中继承来的。run方法的作用是在线程被启动以后,执行预先写入的程序代码。一般而言,run方法所执行的内容被称为Activity,而start方法是用于启动线程的方法。
2.5.3 threading中的Lock类
虽然线程可以在程序的执行过程中极大地提高程序的执行效率,但是其带来的影响却难以忽略。例如在上一个程序中,由于每隔一定时间打印当前的数值,应该逐次打印的数据却变成了2个相同的数值被打印出来,因此需要一个能够解决这类问题的方案出现。
Lock类是threading中用于锁定当前线程的锁定类,顾名思义,其作用是对当前运行中的线程进行锁定,只有被当前线程释放后,后续线程才可以继续操作。
import threading lock = threading.Lock() lock.acquire() lock.release()
类中主要代码如上所示。acquire方法提供了确定对象被锁定的标志,release在对象被当前线程使用完毕后将当前对象释放。修改后的代码如程序2-4所示。
【程序2-4】
可以看到Lock被传递给MyThread,并在run方法中人为锁定当前的线程,必须等当前线程执行完毕后,后续的线程才可以继续执行。程序执行结果如图2-30所示。
图2-30 程序运行结果
可以看到,其中变色的部分,线程2只有等线程1完全结束后,才执行后续的操作。本程序中,Thread1等到Thread0完全结束后,才执行自己的操作。
2.5.4 threading中的join类
join类是threading中用于堵塞当前主线程的类,其作用是阻止全部的线程继续运行,直到被调用的线程执行完毕或者超时。具体代码如程序2-5所示。
【程序2-5】
程序的运行结果如图2-31所示。
图2-31 程序运行结果
其中的time方法设定了当前的时间,当join启动后,堵塞了调用整体进程的主进程,而只有当被堵塞的进程执行完毕后,后续的进程才继续执行。
除此之外,对于线程的使用,Python还有很多其他的方法,例如threading.Event以及threading.Condition等,这些都是在程序设计时能够极大地帮助程序设计人员编写合适程序的工具。限于篇幅,这里不再一一进行介绍,读者可以参考相关图书,在后续的使用过程中,作者会带领读者了解和掌握更多的相关内容。