守护线程解释
- 2025-01-21 09:01:00
- admin 原创
- 77
问题描述:
Python 文档中
这样写道:
可以将线程标记为“守护线程”。此标志的意义在于,当只剩下守护线程时,整个 Python 程序将退出。初始值从创建线程继承。
是否有人能更清楚地解释这是什么意思,或者能提供一个实际例子来说明将线程设置在哪里daemonic
?
请为我澄清一下:所以您不会将线程设置为的唯一情况daemonic
是当您希望它们在主线程退出后继续运行吗?
解决方案 1:
有些线程会执行后台任务,例如发送 keepalive 数据包,或执行定期垃圾收集等。这些线程仅在主程序运行时有用,其他非守护线程退出后就可以终止它们。
如果没有守护线程,您必须跟踪它们,并告诉它们退出,然后程序才能完全退出。通过将它们设置为守护线程,您可以让它们运行并忘记它们,当您的程序退出时,任何守护线程都会自动被杀死。
解决方案 2:
假设您正在制作某种仪表板小部件。作为其中的一部分,您希望它显示电子邮件收件箱中的未读邮件数。因此,您创建了一个小线程,它将:
连接到邮件服务器并询问有多少未读消息。
向 GUI 发出更新后的计数信号。
睡一小会儿。
当您的小部件启动时,它会创建此线程,将其指定为守护进程,然后启动它。因为它是一个守护进程,所以您不必考虑它;当您的小部件退出时,线程将自动停止。
解决方案 3:
我也要在这里补充几点,我认为守护进程线程让大多数人感到困惑的原因之一(至少对我来说)是由于这个词的 Unix 上下文dameon
。
在 Unix 术语中,该词daemon
指的是一旦产生的进程;它会在后台继续运行,而用户可以继续使用前台进程做其他事情。
在 Python 线程上下文中,每个线程在创建后都在后台运行,无论是daemon
还是non-daemon
,区别在于这些线程如何影响主线程。
当您启动一个non-daemon
线程时,它会开始在后台运行,您可以执行其他操作,但是,您的主线程直到所有这些线程完成其执行后才会退出non-daemon
,因此在某种程度上,您的程序或主线程被阻塞了。
使用daemon
线程时,它们仍然在后台运行,但有一个关键区别,即它们不会阻塞主线程。一旦主线程完成执行并且程序退出,所有剩余daemon
线程都将被收割。这使得它们对于您想要在后台执行但希望这些操作在主应用程序退出时自动退出的操作非常有用。
需要注意的一点是,您应该清楚自己在daemon
线程中执行的操作,因为主线程退出时线程也会退出,这可能会给您带来意想不到的惊喜。 清理线程的一种方法daemon
就是使用线程事件将事件设置为退出处理程序,然后检查事件是否在线程内部设置,然后相应地从线程函数中中断。
关于线程的另一件令人困惑的事情daemon
是来自 python 文档的定义。
此标志的意义在于,当只剩下守护线程时,整个 Python 程序就会退出
简单来说,这意味着如果您的程序同时具有daemon
和non-daemon
线程,则主程序将被阻塞并等待,直到所有线程都non-daemon
退出,一旦它们退出,主线程也将退出。这句话还暗示但乍一看并不清楚的是,daemon
一旦主线程退出,所有线程都将自动退出。
解决方案 4:
也许有一个更简单的思考方式:当 main 返回时,如果仍有非守护线程在运行,则您的进程将不会退出。
一点建议:当涉及线程和同步时,干净关闭很容易出错 - 如果可以避免,请避免。尽可能使用守护线程。
解决方案 5:
其他发帖者给出了一些使用守护线程的情况的示例。但我的建议是永远不要使用它们。
这并不是因为它们没有用,而是因为使用它们可能会产生一些不好的副作用。在 Python 运行时开始拆除主线程中的程序后,守护线程仍可执行,从而导致一些非常奇怪的异常。
更多信息请点击这里:
https://joeshaw.org/python-daemon-threads-considered-harmful/
https://mail.python.org/pipermail/python-list/2005-February/343697.html
严格来说,你永远不需要它们,它们只是在某些情况下使实现更容易。
解决方案 6:
引用 Chris 的话:“...当您的程序退出时,所有守护线程都会自动终止。”我认为这总结了一切。使用它们时应小心谨慎,因为它们会在主程序执行完成时突然终止。
解决方案 7:
Chris 已经解释了守护线程是什么,现在我们来谈谈实际用法。许多线程池实现都使用守护线程作为任务工作线程。工作线程是从任务队列执行任务的线程。
工作者需要无限期地等待任务队列中的任务,因为它们不知道新任务何时出现。分配任务的线程(例如主线程)只知道任务何时结束。主线程等待任务队列变空然后退出。如果工作者是用户线程,即非守护线程,程序将不会终止。它将继续等待这些无限期运行的工作者,即使工作者没有做任何有用的事情。将工作者标记为守护线程,主线程将在处理完任务后立即杀死它们。
解决方案 8:
当您的第二个线程是非守护进程时,应用程序的主要主线程无法退出,因为它的退出条件也与非守护进程线程的退出相关。在 Python 中,线程无法被强制终止,因此您的应用程序必须等待非守护进程线程退出。如果您不希望出现这种情况,请将您的第二个线程设置为守护进程,这样它就不会阻止您的应用程序退出。
解决方案 9:
在以下情况下创建守护进程线程:
您需要一个低优先级的线程
你的线程执行特定于后台的任务,更重要的是,
当您希望所有用户线程完成其任务后该线程立即死亡时。
守护线程服务的一些示例: Java 中的垃圾收集、MS Word 中的字数检查器、媒体中的自动保存器、并行文件下载应用程序中的文件下载计数器等。
解决方案 10:
在 Python 中,线程有两种类型:守护线程和非守护线程(有时称为“普通线程”或简称为“线程”)。这些线程类型之间的主要区别在于它们如何影响整个程序的生命周期:
守护线程:这些线程被视为“后台”线程。这些线程在后台运行,不会阻止主程序完成。一旦程序中的所有非守护线程都完成,程序将终止,关闭所有仍在运行的守护线程,无论它们是否已完成任务。
非守护线程:这些是程序的“主”线程。只要这些线程中至少有一个处于活动状态,程序就会继续运行。只有当最后一个非守护线程完成后,整个程序才能终止。
概括:
当没有非守护线程处于活动状态时,整个 Python 程序将终止。这意味着,无论有多少守护线程正在运行,当所有非守护线程都完成执行时,程序都将终止。守护线程无法自行保持程序运行;它们用于不需要停止程序终止的后台任务。
例子:
#!/usr/bin/env python3
""" Barron finishes cooking while Olivia cleans """
import threading
import time
def kitchen_cleaner():
while True:
print('Olivia cleaned the kitchen.')
time.sleep(1)
if __name__ == '__main__':
olivia = threading.Thread(target=kitchen_cleaner)
olivia.daemon = True
olivia.start()
print('Barron is cooking...')
time.sleep(0.6)
print('Barron is cooking...')
time.sleep(0.6)
print('Barron is cooking...')
time.sleep(0.6)
print('Barron is done!')
解决方案 11:
尽管这个问题已经有 12 个答案,并且 @Rohit 的回答很好地解释了情况,但是当我检查https://docs.python.org/3/library/socketserver.html中的服务器示例代码时,我仍然感到有点困惑。
if __name__ == "__main__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
with server:
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
那里的评论说,通过设置server_thread.daemon = True
我可以“在主线程终止时退出服务器线程”。
这就是我遇到的情况,我有一个使用线程运行 HTTPServer 的 GUI 程序。我最初没有设置守护进程属性,因为我认为如果我设置了它,即使父线程(我的 GUI 线程)退出后,HTTPServer 线程仍会在后台继续运行。结果恰恰相反,即使我终止了我的 GUI 程序,我的 http 服务器使用的端口仍处于使用状态,因此我无法重新启动我的程序。