【问题标题】:Python Threading/DaemonPython 线程/守护进程
【发布时间】:2015-04-30 06:10:05
【问题描述】:

自学编程的学生,所以我为所有业余错误道歉。我想学习一些更深层次的主题,所以我试图理解线程和异常处理。

import threading
import sys
from time import sleep
from random import randint as r

def waiter(n):
    print "Starting thread " + str(n)
    wait_time = r(1,10)
    sleep(wait_time)
    print "Exiting thread " + str(n)

if __name__=='__main__':
    try:
        for i in range(5):
            t = threading.Thread(target=waiter, args=(i+1,))
            t.daemon = True
            t.start()
            sleep(3)
        print 'All threads complete!'
        sys.exit(1)
    except KeyboardInterrupt:
        print ''
        sys.exit(1)

这个脚本只是在随机时间后启动和停止线程,如果它收到^C,它将终止程序。我注意到当某些线程完成时它不会打印:

Starting thread 1
Starting thread 2
Starting thread 3
Exiting thread 3
Exiting thread 2
Starting thread 4
Exiting thread 1
Exiting thread 4
Starting thread 5
All threads complete!

在这个例子中,它从不声明它退出线程 5。我发现如果我注释掉 t.daemon = True 语句,我可以解决这个问题,但是异常处理会等待任何线程完成。

Starting thread 1
Starting thread 2
^C
Exiting thread 1
Exiting thread 2

我可以理解,在处理线程时,最好在退出之前完成他们正在处理的事情,但我只是好奇这是为什么。我非常感谢有关线程和守护进程性质的任何答案,以指导我的理解。

【问题讨论】:

  • 我怀疑你在实际上寻找的是一种让线程通常像普通线程一样工作的方法,但是当你真的这样做时,它可以像守护线程一样被异步杀死想要(例如,如果用户迫切需要 ^C 应用程序),对吗?这很难做到,但并非不可能。
  • @abarnert 这不是我最初想要做的,但现在你提到它,我想看看如何做到这一点。目前我真的不需要这些知识(不需要编写任何重要或有价值的脚本),我只是想加深理解,因为我喜欢这种事情:)
  • 例如:使用守护线程,但在大部分代码中假装你没有。通过等待所有工作人员向您发出他们已完成的信号(使用一些同步对象)退出。提前退出,发出所有退出的信号(请注意,这意味着您需要重构工作人员,以便他们定期检查标志和同步对象),然后等待他们。通过向他们发出信号来提前快速退出,等待他们超时,然后退出(这意味着他们几乎总是需要在超时内检查标志)。只需退出即可尽早退出。
  • @abarnert ...好吧,我确定我现在的工作方式会很好,哈哈,谢谢你的解释。我不明白其中的大部分内容,但在 5 年的时间里,当我从某个地方真正学到这些东西时,我会回顾你的评论并想,“啊,是的,我是多么愚蠢。”
  • 或者,如果我们仍在编写低级别的并发代码(线程和同步对象),它将适用于具有 16 个全核、256 个微核的大规模非统一系统,以及数以千计的 SIMD 通道和 GPU 通道,而我今天所知道的一切都将是无可救药的错误。 :)

标签: python multithreading daemon


【解决方案1】:

守护线程的整个要点是,如果它在主线程完成时还没有完成,它就会被立即杀死。引用the docs:

可以将线程标记为“守护线程”。这个标志的意义在于,当只剩下守护线程时,整个 Python 程序就退出了。初始值继承自创建线程。该标志可以通过 daemon 属性或 daemon 构造函数参数设置。

注意守护程序线程在关闭时突然停止。它们的资源(如打开的文件、数据库事务等)可能无法正常释放。如果您希望线程优雅地停止,请将它们设为非守护进程并使用合适的信号机制,例如事件。

现在,看看你的逻辑。主线程在启动线程 5 后仅休眠 3 秒。但线程 5 可以休眠 1-10 秒。因此,大约 70% 的时间,当主线程唤醒、打印“所有线程完成!”并退出时,它还没有完成。但是线程 5 仍然休眠了 5 秒。在这种情况下,线程 5 将被终止,而不会打印“Exiting thread 5”。

如果这不是你想要的行为——如果你想让主线程等待所有线程完成——那么不要使用守护线程。

【讨论】:

  • 我明白了,这是有道理的。所以我假设任何处理关键和敏感数据的线程(即不应被突然杀死的线程)都不应该被标记为守护线程?
  • @dbishop:一般来说,是的。但是,如果您的敏感数据处理线程可以“在一边”分批完成其工作,然后以原子方式提交每一个(例如,写入临时文件,然后将该临时文件移动到永久位置),它可以安全地被杀死。
  • @dbishop:另外,可以使用同步机制让主线程向工作人员发出信号,通知工作人员该关闭了,并给他们一些时间来响应,只有在出现情况时才会放弃并让他们被杀死他们的反应不够快。这种设计对于需要“非常可靠”但不是 100% 的东西很常见,例如 Web 服务器连接处理程序。
  • 明白了。我想我现在理解得更好了。感谢所有的帮助!非常感谢!
猜你喜欢
  • 1970-01-01
  • 2011-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-10
  • 1970-01-01
相关资源
最近更新 更多