[Python学习日记-92] 并发编程之多线程 —— 守护线程
简介
守护线程(Daemon Thread)是一个不容忽视的概念,它有着独特的运行机制和应用场景,理解并掌握守护线程,能让我们在编写多线程程序时更加得心应手,合理地管理线程资源,优化程序性能。在前面介绍进程时我们也介绍过守护进程,本次要介绍的守护线程与其类似,都是遵循着“守护线程/进程会等待主线程/进程运行完毕后被销毁”。接下来,就让我们一同揭开守护线程的神秘面纱。
守护线程的概念
在进程的学习当中也有守护的这一概念,当时我们把其称为守护进程,而守护线程其实和守护进程理念类似,都是子的守护主的,主的结束了子的也一起结束,即子线程的生命周期依赖于主线程,当主线程运行完毕时,无论守护线程是否运行完毕,都会被立即终止。需要强调的是,这里的运行完毕并非指终止运行,对于主进程来说,运行完毕指的是主进程代码运行完毕,进一步来说,主进程在其代码结束后就已经算运行完毕了,同时守护进程在此时也被回收,然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源才会结束(否则会产生僵尸进程);对于主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程(用户线程)统统运行完毕,主线程才算运行完毕,此时守护线程就会被回收,总的来说主线程的结束意味着进程的结束,进程整体的资源都将被回收。
守护线程通常用于执行一些辅助性的、后台支持的任务,比如垃圾回收、日志监控、资源清理等。这些任务不需要等待其完全执行完毕就可以结束程序,一旦主线程完成其核心工作,守护线程的使命也就结束了。例如,在一个服务器程序中,可能有一个守护线程负责定期清理过期的缓存数据,当服务器的主线程停止服务时,这个清理缓存的守护线程也应该随之结束,而不需要等待它完成当前的清理任务。
守护线程的实现方式
一、通过 setDaemon() 方法设置
示例代码如下:
from threading import Thread
import time
def task():
while True:
time.sleep(1)
print("子线程正在执行...") # 还没执行主线程已经结束了,所以不会打印出来
if __name__ == "__main__":
t = Thread(target=task)
t.setDaemon(True) # 将线程t设置为守护线程,必须在t.start()之前设置
t.start()
print("主线程结束")
print(t.is_alive())
代码输出如下:
在上述代码中,我们定义了一个 task 函数,该函数在一秒后将打印信息。然后创建了一个线程t来执行 task 函数,并通过 setDaemon(True) 将其设置为守护线程。主线程启动子线程后,打印 “主线程结束”。由于子线程是守护线程,当主线程结束时,子线程会立即终止,即使后面的信息还没有打印出来。
注意:setDaemon() 在新版本 Python3 中已经废弃了。
二、通过 daemon 参数设置
示例代码如下:
from threading import Thread
import time
def task():
while True:
time.sleep(1)
print("子线程正在执行...") # 还没执行主线程已经结束了,所以不会打印出来
if __name__ == "__main__":
t = Thread(target=task)
t.daemon = True # 将线程t设置为守护线程,必须在t.start()之前设置
t.start()
print("主线程结束")
print(t.is_alive())
代码输出如下:
在上述代码与通过 setDaemon() 方法设置的代码功能一致,所以输出也一致。
三、继承 Thread 类时设置守护线程
示例代码如下:
from threading import Thread
import time
class MyThread(Thread):
def __init__(self):
super().__init__()
self.daemon = True # 在初始化方法中设置为守护线程
def run(self):
while True:
print("自定义子线程正在执行...")
time.sleep(1)
if __name__ == "__main__":
t = MyThread()
t.start()
time.sleep(3)
print("主线程结束")
代码输出如下:
在这个示例中,我们自定义了 MyThread 类继承自 Thread 类,并在 __init__ 方法中设置 self.daemon = True,将自定义线程设置为守护线程。在 run 方法中每隔一秒无限循环的输出指定信息,主线程启动子线程后休眠 3 秒结束,此时守护线程也会随之终止。
主线程等待非守护线程(用户线程)的例子
示例代码如下:
from threading import Thread
import time
def foo():
print('foo子线程开始了...')
time.sleep(1)
print('foo子线程结束了...')
def bar():
print('bar子线程开始了...')
time.sleep(3)
print('bar子线程结束了...')
if __name__ == '__main__':
t1 = Thread(target=foo)
t2 = Thread(target=bar)
t1.daemon = True
t1.start()
t2.start()
print('主线程所在进程的代码执行完了...')
代码输出如下: