在关闭交互式Python会话时结束非守护程序线程

2022-04-17 00:00:00 python python-multithreading

问题描述

请考虑以下代码:

#! /usr/bin/env python3

import threading
import time

class MyThread(threading.Thread):
    def __init__(self):
        super().__init__()
        self._quit_flag = False
    def run(self):
        while not self._quit_flag:
            print("Thread is alive!")
            time.sleep(0.500)
    def request_quit(self):
        self._quit_flag = True

mt = MyThread()
mt.start()
在将其另存为‘test.py’并运行‘python3-i test.py’之后,我得到了一个交互会话,在该会话中,线程定期打印"Thread is live!"消息。

当我按Ctrl-D或Run Exit()时,python3进程不会终止,因为线程仍在运行。我想解决这个问题。

但是,我不想使该线程成为守护进程线程--我希望能够正确地结束/加入该线程,因为在我要解决的实际情况中,我希望很好地终止网络连接。

有没有办法做到这一点?例如,在REPL循环终止之后、就在主线程退出之前执行的某个挂钩是否可用?


解决方案

好的,我自己找到了一种方法。

查看Python源代码,发现在关闭时,在完成进程关闭之前,Python会尝试加入()所有非守护进程线程。这是有意义的,除了如果有一种有文档记录的方式来通知这些线程,这会更有用。但事实并非如此。

但是,可以重新实现从Thread派生的自己的类的Join()方法,这可以作为通知子线程应该关闭自身的一种方式:

class MyThread(threading.Thread):
    def __init__(self):
        super().__init__()
        self._quit_flag = False
    def __del__(self):
        print("Bye bye!")
    def run(self):
        while not self._quit_flag:
            print("Thread is alive!",  threading.active_count(), threading.main_thread().is_alive())
            for t in threading.enumerate():
                print(t.is_alive())
            time.sleep(0.500)
    def request_quit(self):
        self._quit_flag = True

    def join(self):
        self.request_quit()
        super().join()

然而,这种解决问题的方法几乎是一种黑客行为,它忽略了‘线程’模块文档(https://docs.python.org/3/library/threading.html)中的以下段落:

Thread类表示在单独的 控制的线索。有两种方式可以指定活动:按 将可调用对象传递给构造函数,或通过重写 子类中的run()方法。没有其他方法(除了 构造函数)应在子类中重写。换句话说,只有 重写此类的__init__()和run()方法。

然而,它确实工作得很好。

相关文章