Python多线程/守护进程

4

我是一名自学编程的学生,所以我很抱歉可能存在一些业余的错误。我想学习一些更深入的主题,因此我正在尝试理解线程和异常处理。

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

我可以理解在处理线程时,最好让它们完成正在处理的任务后再退出,但是我只是好奇为什么要这样做。我非常感谢有关线程和守护进程本质的任何答案,以指导我的理解。


我猜你实际上想要的是一种线程,它通常像普通线程一样运行,但在需要异步终止时可以像守护线程一样被杀死,对吗?这很棘手,但并非不可能。 - abarnert
@abarnert,这不是我最初想做的事情,但既然你提到了,我想看看如何完成。目前我并没有任何需要这种知识的需求(没有编写任何重要或有价值的脚本),我只是想开发一种理解,因为我喜欢这种东西 :) - dbishop
例如:使用守护线程,但在大部分代码中假装你没有使用。通过等待所有工作线程发出完成信号(使用某些同步对象)来退出。提前退出并通知它们全部退出(请注意,这意味着您需要重构工作线程,以便它们定期检查标志和同步对象),然后等待它们。通过发出信号、等待超时并退出(这意味着它们几乎总是需要在该超时时间内检查标志)来尽早快速退出。通过直接退出来立即退出。 - abarnert
@abarnert... 好吧,我相信现在我正在做的方式会很好用,哈哈,谢谢你的解释。虽然我不太理解大部分内容,但是五年后当我真正从某个地方学到这些东西时,我会回顾你的评论并想,“啊,我当时多傻啊。” - dbishop
或者,如果我们仍然在低级别上编写并发代码(线程和同步对象),那么它将用于具有16个完整核心、256个微内核、数千个SIMD通道和GPU通道的大规模非均匀系统,而我今天所知道的一切都将是毫无意义的。 :) - abarnert
1个回答

5
守护线程的整个意义在于,如果主线程完成时它还没有完成,那么它会被迅速杀死。引用文档中的话:
“线程可以标记为‘守护线程’。这个标志的重要性在于,当只剩下守护线程时,整个Python程序就会退出。初始值是从创建线程继承的。该标志可以通过daemon属性或守护进程构造函数参数进行设置。”
请注意,守护线程在关闭时会被突然停止。它们的资源(如打开的文件、数据库事务等)可能无法正确释放。如果你想让你的线程优雅地停止,请将它们设为非守护线程,并使用适当的信号机制,如事件。
现在看看你的逻辑。主线程启动线程5后只睡眠3秒钟。但是线程5可以睡眠1-10秒钟。所以,大约70%的时间,当主线程醒来、打印“All threads complete!”并退出时,线程5还没有完成。但是线程5仍然会再睡5秒钟。在这种情况下,线程5将被杀死,而不会打印“Exiting thread 5”。
如果这不是您想要的行为——如果您希望主线程等待所有线程完成——那么请不要使用守护线程。

我明白了,那很有道理。所以我认为任何处理关键和敏感数据的线程(即不应突然终止的线程)都不应标记为守护线程? - dbishop
@dbishop:一般来说,是的。但是,如果您的敏感数据处理线程可以批量“在侧面”完成其工作,然后原子地提交每个批次(例如,写入临时文件,然后将该临时文件移动到永久位置),则可以安全地终止它。 - abarnert
@dbishop:此外,可以使用同步机制来让主线程通知工作线程该关闭了,并给它们一些时间来响应,只有当它们响应不够快时才会放弃并让它们被杀死。这种设计对于需要“相当可靠”但不是100%的东西很常见,比如Web服务器连接处理程序。 - abarnert
明白了,我现在有点理解了。感谢您的所有帮助!非常感激! - dbishop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接