python多线程编程: 如何暴力但不失优雅地关闭线程

多i线程编程中,常常遇到的1个头痛问题是,主线程退出后,子线程未能正常退出,造成一些数据丢失,文件破坏的隐患,或者成为僵尸进程,占用系统资源。
前面文章介绍了几种优雅关闭线程的方法,但也有一些网友提出疑问:是否可以暴力但不留隐患杀死线程?, 答案是:Yes!

解决方法说明:
1)所谓暴力就是按 Ctrl+C, 或者 Linux下发送kill -9 强制中止程序信号等。
2)通过python的signal模块,可以实时捕捉到Ctrl+C信号,并触发回调函数。将开关变量置为关闭,执行clean code,再关闭线程。Linux系统下,将信号 signal.SIGTERM 改为 signal.SIGKILL即可,用kill -9 命令直接杀死线程。
3)为了让代码更健壮,使用了自定义异常类来负责主线程的退出。

下面是完整代码,在win10下测试,支持按Ctrl+C键, 或者通过任务面板关闭进程,均可安全地关闭线程,杜绝隐患。

from math import e
import time
import threading
import signal
 
 
class Task(threading.Thread):
 
    def __init__(self):
        threading.Thread.__init__(self)
 
        # shutdown_flag 用于关闭线程
        self.shutdown_flag = threading.Event()
 
        # ... Other thread setup code here ...
 
    def run(self):
        print('Thread #%s started' % self.ident)
 
        while not self.shutdown_flag.is_set():
            # 线程代码可以放在这里            
            print(f"Thread #{self.ident} is running...")
            time.sleep(1)
 
        # ... Clean code put here ...
        print('Thread #%s stopped' % self.ident)
 
 
class ServiceExit(Exception):
    """
    自定义1个异常类,用于退出线程
    """
    def __init__(self,message="force to quit"):
        self.message = message
        super(ServiceExit,self).__init__(self.message)
    
    # __str__ is to print() the message
    def __str__(self):
        return(repr(self.message))
 
 
def handler_quit(signum, frame):
    """信号处理函数"""
    print('Caught signal %d' % signum)
    raise ServiceExit("当前线程被强制退出...")
 
 
def main():
 
    # 注册信号回调函数
    signal.signal(signal.SIGTERM, handler_quit)
    signal.signal(signal.SIGINT, handler_quit)
 
    print('Starting main program')
 
    # Start the sub threads
    try:
        t1 = Task()
        t2 = Task()
        t1.start()
        t2.start()
 
        # 保持主线程运行,否则无法收到信号
        while True:
            time.sleep(0.5)
 
    except ServiceExit as e:
        t1.shutdown_flag.set()
        t2.shutdown_flag.set()
        # Wait for the threads to close...
        t1.join()
        t2.join()
        print(e.message)
 
    print('Exiting main program')
 
 
if __name__ == '__main__':
    main()

运行output 如下

Starting main program
Thread #119072 started
Thread #119072 is running...
Thread #119076 started
Thread #119076 is running...
Thread #119076 is running...
Thread #119072 is running...
Thread #119072 is running...
Thread #119076 is running...
Thread #119076 is running...
Thread #119072 is running...
Caught signal 2
Thread #119072 stopped
Thread #119076 stopped
当前线程被强制退出...
Exiting main program

你可能感兴趣的:(python,开发语言,django,flask,tornado,scrapy)