在线程的生命周期中,从创建到执行以及最终终止,线程通常处于四种状态之一:开始态、可调度状态、阻塞态和终止态。
父线程和子线程
当一个新的进程或程序开始运行时,它将以一个线程开始,这个线程被称为主线程。然后主线程可以启动或生成其他线程,这被称为子线程,它们同样是进程的一部分,但独立执行其他任务。如果需要,这些线程还可以生成自己的子线程,当每个线程完成执行时,将通知它们的父线程,最后主线程终止整个任务。父线程和子线程关系如下图所示:
线程的四种基本状态
不同的编程语言可能会使用不同的名称,并且还有一些额外的名称,但通常在线程的生命周期中,从创建到执行以及最终终止,线程通常处于四种状态:开始态、可调度态、阻塞态和终止态。
- 开始态:主线程需要产生或创建另一个线程来辅助完成整个任务,子线程将以新状态开始,Python语言要求在创建线程后显式启动它。
- 可调度态:处于可运行状态,这意味着操作系统可以安排资源调度执行。通过上下文与其他线程交换,以便在处理器上运行。
- 阻塞态:当线程需要等待事件发生时,如外部输入或计时器,或者调用子线程的
join()
方法进入阻塞状态,当进入阻塞态时,线程不会使用任何CPU资源。 - 终止态:线程在完成执行或异常中止时进入终止状态。
线程的状态转换如下图所示:
线程生命周期示例
线程的定义有两种方式,分别是函数方式和类定义方式。函数方式在创建新线程时为它分配一个函数,即它将要执行的代码,我们在前面的课程中已经介绍过了,感兴趣的读者请参阅《一个Python多线程运行实例》。本文演示Python线程的生命周期将使用class方式来定义线程,具体代码如下:
#!/usr/bin/env python3
""" Two threads cooking soup """
import threading
import time
class ChefOlivia(threading.Thread):
def __init__(self):
super(ChefOlivia, self).__init__()
def run(self):
print('Olivia started & waiting for sausage to thaw...')
time.sleep(3)
print('Olivia is done cutting sausage.')
# main thread a.k.a Barron
if __name__ == '__main__':
print("Barron started & requesting Olivia's help.")
olivia = ChefOlivia()
print(" Olivia alive?", olivia.is_alive())
print("Barron tells Olivia to start.")
olivia.start()
print(" Olivia alive?", olivia.is_alive())
print("Barron continues cooking soup.")
time.sleep(0.5)
print(" Olivia alive?", olivia.is_alive())
print("Barron patiently waits for Olivia to finish and join...")
olivia.join()
print(" Olivia alive?", olivia.is_alive())
print("Barron and Olivia are both done!")
这个示例程序,模拟了Olivia和Barraon之间线程协作,Olivia需要帮助完成切片香肠,最后Brrraon完成做汤的算法。在Python中通过类创建线程需要定义一个继承threading.Thread类
的子类,并且需要重载init
和 run
方法,在本例的init
方法中,只需使用super
函数执行父线程初始化即可。
首先Barron启动并请求Olivia的帮助,然后创建一个新的ChefOlivia对象。此时ChefOlivia还没有开始,Barron显示调用Olivia的start()方法启动子线程。然后Barron继续煮汤,并花费了0.5秒钟的时候。因为Olivia切香肠需要3秒钟时间,Barron会在其之前完成,为了最终完成整个做汤的任务,Barron会同步等待Olivia,通过调用join()方法,主线程进入阻塞状态,直到Olivia完成任务后,最后程序终止。
回到代码中,我们在控制台中运行此程序,同时加入了判断Olivia状态的打印语句,is_alive
方法返回一个布尔值,指示该线程当前状态。最终打印结果输出如下:
$ python thread_lifecycle.py
Barron started & requesting Olivia's help.
Olivia alive? False
Barron tells Olivia to start.
Olivia started & waiting for sausage to thaw...
Olivia alive? True
Barron continues cooking soup.
Olivia alive? True
Barron patiently waits for Olivia to finish and join...
Olivia is done cutting sausage.
Olivia alive? False
Barron and Olivia are both done!
注意Olivia的线程在最初创建后并没有启动,状态显示是False,需要Barron线程显示调用start
方法,之后线程状态显示存活。Olivia的线程正sleeping时,Python认为子线程状态仍然是存活的。Olivia调用join
,主线程进入阻塞,并等待子线程其完成,Olivia运行完成,主线程继续执行,直至整个程序结束。整个程序的生命周期状态如下图所示:
结束语
本文介绍了主线程和子线程的关系,以及线程在操作系统中的四种基本状态,讲解了除了使用函数定义线程之外,使用class定义子线程的方法,最后展示了一个线程的生命周期代码实例。