多线程
线程是操作系统能够调度的最小单位,其是进程中的单一顺序的控制流。通常情况下,一个进程包括至少一个线程,如果有多个线程,其中包括一个主线程。同一个进程内的所有线程共享系统资源,但它们有各自独立的栈、寄存器环境和本地存储。多线程的好处是可以同时执行多个任务,如果系统有多个计算单元,那么多个线程可以在各自的计算单元并行运行,这样可以极大提升系统的处理效率。
线程一般包括程序、数据和线程控制块,其中线程控制块包括以下信息:
•线程状态。
•在线程处于非运行态时,其上次运行结束时的处理器状态,主要是寄存器的状态。
•堆栈信息。
线程是独立调度和分派的基本单位,线程切换消耗的资源比较少。在多核系统中,多线程可以完全并发执行,这样可以完全发挥硬件的计算能力。同一个进程内的线程共享很多资源,如文件、定时器等,所以线程通信是很方便的。
Python系统自带了两个线程相关的包,一个是thread,其要求用户提供一个函数作为线程的入口函数;另外一个包是threading,其要求的输入是一个派生自threading.Thread类的实例对象。
主线程
在一个进程中,第一个启动的线程就是主线程,其具有如下特点:
•其他线程都是由主线程直接或者间接创建出来的,我们可以画一棵线程树,主线程就是树的根,其他线程就是树上的节点。
•主线程退出会导致进程退出,该进程内的所有其他线程会被强制退出。
线程状态
线程可以处于不同的状态,随着时间推移,线程在不同状态之间迁移。线程有5种状态,分别是新建、就绪、运行、阻塞和结束。
线程在thread包中,需要定义一个函数作为线程的入口。我们可以编写下面的一个函数,其每隔一秒钟就打印一句话,打印10次后退出。代码如下:
图片1另外,如果需要启动某个线程,可以使用下面的方法:
start_new_thread(function,args[,kwargs])
该方法的第一个参数function表示要执行的函数,如上面定义的函数名,该函数将作为线程的入口函数使用。args和kwargs是该函数的参数,args是必须的,类型是元组;kwargs是可选的,类型是字典。
函数start_threads()将会启动两个线程,每个线程都是以thread_entry()作为入口函数。下面是完整的代码。
图片2线程的退出
线程的退出方式:
•入口函数执行完毕。
•线程抛出没有处理的异常。
•线程内部调用_thread.exit_thread()来退出。
threading包
相对于thread包,threading包提供了更多的功能。该包的用法基本分成两步,第一步是构造一个threading.Thread实例对象,这时该对象对应的线程就处于“新建”状态;第二步是操作该对象,如调用start()来将该线程转换到“就绪”状态。
创建基于现有的threading.Thread类的实例对象,主要需要提供入口函数和对应的参数。入口函数仍复用前面的函数,代码如下:
图片3死锁
所谓死锁就是线程一直无限期地等待某个资源。最简单的死锁现象就是一个线程等待一个自己已经拿到的锁。由于该锁已经被自己拿到了,所以第二次申请该锁时会被放到等待队列中,但这个等待的时间是永远。