10.3.3 守护与非守护线程
到目前为止,示例程序都在隐式地等待所有线程完成工作之后才退出。不过,程序有时会创建一个线程作为守护线程(daemon),这个线程可以一直运行而不阻塞主程序退出。如果一个服务不能很容易地中断线程,或者即使让线程工作到一半时中止也不会造成数据损失或破坏(例如,为一个服务监控工具生成“心跳”的线程),那么对于这些服务,使用守护线程就很有用。要标志一个线程为守护线程,构造线程时便要传入daemon=True或者要调用它的setDaemon()方法并提供参数True。默认情况下线程不作为守护线程。
import threading
import time
import logging
def daemon():
logging.debug('Starting')
time.sleep(0.2)
logging.debug('Exiting')
def non_daemon():
logging.debug('Starting')
logging.debug('Exiting')
logging.basicConfig(
level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
d = threading.Thread(name='daemon',target=daemon,daemon=True)
t = threading.Thread(name='non-daemon',target=non_daemon)
d.start()
t.start()
这个代码的输出中不包含守护线程的“Exiting”消息,因为在从sleep()调用唤醒守护线程之前,所有非守护线程(包活主线程)已经退出。
运行结果:
要等待一个守护线程完成工作,需要使用join()方法。
import threading
import time
import logging
def daemon():
logging.debug('Starting')
time.sleep(0.2)
logging.debug('Exiting')
def non_daemon():
logging.debug('Starting')
logging.debug('Exiting')
logging.basicConfig(
level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
d = threading.Thread(name='daemon',target=daemon,daemon=True)
t = threading.Thread(name='non_daemon',target=non_daemon)
d.start()
t.start()
d.join()
t.join()
使用join()等待守护线程退出意味着他有机会生成它的“Exiting”消息。
运行结果:
默认地,join()会无限阻塞,或者,还可以传入一个浮点值,表示等待线程在多长时间 (秒数)后变为不活动。即使线程在这个时间段内未完成,join()也会返回。
import threading
import time
import logging
def daemon():
logging.debug('Starting')
time.sleep(0.2)
logging.debug('Exiting')
def non_daemon():
logging.debug('Starting')
logging.debug('Exiting')
logging.basicConfig(
level=logging.DEBUG,
format='(%(threadName)-10s) %(message)s',
)
d = threading.Thread(name='daemon',target=daemon,daemon=True)
t = threading.Thread(name='non-daemon',target=non_daemon)
d.start()
t.start()
d.join(0.1)
print('d.isAlive()',d.isAlive())
t.join()
由于传入的超时时间小于守护线程睡眠的时间,所以join()返回之后这个线程仍“活着”。
运行结果: