python线程crash后的重运行

本文介绍了一种在Python中监控并自动重启异常线程的方法,通过创建一个专用线程来检查其他线程的状态,一旦发现某个线程崩溃,便立即创建新的线程替代之,确保系统的稳定运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前在写一些业务逻辑的时候, 总会碰到一些很诡异的bug, 比如说, 我们的服务可能要同时做那么几件事情, 当然大家现在都流行微服务了, 会把不同的事件抽取成为服务, 可惜我们没有, 那么有时因为服务器网络故障, 所以你的客户端请求服务端的时候crash了, 而你可能整个流程都是在一个线程里面的, 且这个流程都是围绕着客户端和服务端来展开的, 所以之前采取的方法就是抽取出来一个方法, 在外面加上一个while - try - except - else - break, 但是这么写就有点不大好用, 而且传说python的while True是很占CPU的, 所以几番摸索下, 倒腾出了另外一种方式, 代码如下:

import threading
import time

class WThread(threading.Thread):
    # 打印出创建线程的名字, 用于调试
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print(self.name + " created")

    # 线程回收时打印信息, 便于确认线程已回收
    def __del__(self):
        print("线程释放了..." + self.name)


# 一个注定会crash的方法, 占用一个线程
def show():
    for i in range(10):
        print("------------")
        _ = 1/(9-i)
        time.sleep(0.5)


# 监测其他线程是否退出的方法, 如果线程退出, 则再开启一个线程, 并添加至列表中
def run(li):
    while True:
        for t in li:
            if t[1] is not None:
                if not t[0].is_alive():
                    sub_t = WThread(target=t[1])
                    sub_t.setDaemon(True)
                    sub_t.start()
                    li.remove(t)
                    li.insert(0, (sub_t, t[1]))
        time.sleep(1)

# 另一个正常线程
def say():
    while True:
        print("------------")
        time.sleep(1)

# 主函数, 创建三个线程, 一个用于监听, 一个正常, 还有一个异常
def main():
    li = []
    t = WThread(target=show)
    t.setDaemon(True)
    t.start()
    li.append((t, show))

    t1 = WThread(target=say)
    t1.setDaemon(True)
    t1.start()
    li.append((t1, say))

    t2 = WThread(target=run, args=(li, ))
    t2.start()
    li.append((t2, None))

    t2.join()

if __name__ == '__main__':
    main()

总结代码的意思是, 单独开启一个线程去监听其他线程是否crash, 如果crash的话, 就直接新建一个线程, 然后把原来的线程从数据中移除, 把新建的线程添加到列表中。

经过测试, 发现上面的代码创建的线程资源都能够正常回收, 不会存在强引用的情况(其他资源强引用的情况需要自己监测), 但是还有个问题, 在遍历中, 是不推荐对列表进行修改的, 会影响列表的效果, 但是一般在这里影响不是太大...

总结: 最好不要自己造轮子, 网上有很多更优方案, 我这只是为了应对前期需求叠加的产物, 实际开发中, 尽量做到先调研清楚需求, 然后想好架子怎么搭, 然后再动手, 是在碰到需求不断叠加的情况, 也可以做服务拆分, 真的到了我这种地步, 请谨慎处理, 做好测试, 不然等待你的就是修不完的BUG...

 

更新:

基于以上考虑更新了一下代码:

import threading
import time

class WThread(threading.Thread):
    # 打印出创建线程的名字, 用于调试
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print(self.name + " created")

    # 线程回收时打印信息, 便于确认线程已回收
    def __del__(self):
        print("线程释放了..." + self.name)


# 一个注定会crash的方法, 占用一个线程
def show():
    for i in range(10):
        print("------------")
        _ = 1/(9-i)
        time.sleep(0.5)


# 监测其他线程是否退出的方法, 如果线程退出, 则再开启一个线程, 并添加至列表中
def run(li):
    while True:
        append_li = []
        rm_li = []
        for t in li:
            if t[1] is not None:
                if not t[0].is_alive():
                    sub_t = WThread(target=t[1])
                    sub_t.setDaemon(True)
                    sub_t.start()
                    rm_li.append(t)
                    append_li.append((sub_t, t[1]))
        # 不修改外部引用, 不在遍历时修改列表
        for d_item in rm_li:
            if d_item in li:
                li.remove(d_item)
        for a_item in append_li:
            li.append(a_item)
        time.sleep(1)

# 另一个正常线程
def say():
    while True:
        print("------------")
        time.sleep(1)

# 主函数, 创建三个线程, 一个用于监听, 一个正常, 还有一个异常
def main():
    li = []
    t = WThread(target=show)
    t.setDaemon(True)
    t.start()
    li.append((t, show))

    t1 = WThread(target=say)
    t1.setDaemon(True)
    t1.start()
    li.append((t1, say))

    t2 = WThread(target=run, args=(li, ))
    t2.start()
    li.append((t2, None))

    t2.join()

if __name__ == '__main__':
    main()

里面修改了run()方法, 之所以那么修改,是基于个人的一点小习惯, 尽量在引用传参时不改变引用变量, 这里比较特殊, 实际是一个死循环, 但是考虑到平时的习惯, 所以还是这么写了, 实际上完全可以考虑另外创建一个数组把原来的li替换掉, 但是这样li内部的情形不会反馈到引用上面, 对于其他地方想使用li的情况来说, 无法做到完全同步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值