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

时间:2018-05-23 10:31:50

标签: 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 alive!”消息。

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

然而,我不想让线程成为守护线程 - 我希望能够正确地结束/加入线程,因为在我正在解决的现实案例中,我想很好地终止网络连接。

有办法做到这一点吗?例如,在REPL循环终止之后,就在主线程退出之前执行了一些可用的挂钩?

3 个答案:

答案 0 :(得分:2)

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

查看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()

然而,解决这个问题的方法实际上是一个黑客攻击,它忽略了'threading'模块docs(https://docs.python.org/3/library/threading.html)中的以下段落:

  

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

然而,它确实很有效。

答案 1 :(得分:1)

我不确定这是否可以接受,但我能看到你的问题的唯一方法是从你的脚本而不是-i标志启动交互式控制台。然后,在终止交互式会话后,您将能够继续执行退出程序:

import threading
import time
from code import InteractiveConsole

def exit_gracefully():
    print("exiting gracefully now")
    global mt
    mt.request_quit()
    mt.join()


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()
InteractiveConsole(locals=locals()).interact()
exit_gracefully()

这将在没有-i标志的情况下执行,仅作为

python3 test.py

它仍然会像python -i那样为您提供交互式控制台,但现在脚本在交互式shell退出后执行exit_gracefully()

答案 2 :(得分:1)

我认为你可以这样做的方法是使用atexit注册一个清理函数,并将你的线程设置为一个守护进程,例如:

#! /usr/bin/env python3

import threading
import time
import atexit

class MyThread(threading.Thread):
    def __init__(self):
        super().__init__()

        # this ensures that atexit gets called.
        self.daemon = True
        self._quit_flag = False

    def run(self):
        while not self._quit_flag:
            print("Thread is alive!")
            time.sleep(0.500)
        print("cleanup can also go here")

    def request_quit(self):
        print("cleanup can go here")
        self._quit_flag = True


mt = MyThread()
def cleanup():
    mt.request_quit()
    mt.join()
    print("even more cleanup here, so many options!")

atexit.register(cleanup)
mt.start()
相关问题