python主线程捕获子线程异常

2024-06-15 02:12

本文主要是介绍python主线程捕获子线程异常,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

问题抛出:子线程出现异常,主线程仍会正常进行吗

设计实现python主线程捕获子线程异常

需求

问题

解决思路

示例代码


问题抛出:子线程出现异常,主线程仍会正常进行吗

结论:子线程出现异常,主线程仍会正常进行

例子1

测试代码

import threadingdef func(x, m_file):y = 100/(x-5)m_file.write(str(x) + "\n")if __name__ == "__main__":file = open("./test.txt", "w")th_list = []for i in range(0, 10):th = threading.Thread(target=func, args=(i, file))th.start()th_list.append(th)for th in th_list:th.join()file.close()

运行完后报错提示

D:\software\python_3.10.1_32\python.exe D:\hutao\projects\clear_market_data\test.py 
Exception in thread Thread-6 (func):
Traceback (most recent call last):File "D:\software\python_3.10.1_32\lib\threading.py", line 1009, in _bootstrap_innerself.run()File "D:\software\python_3.10.1_32\lib\threading.py", line 946, in runself._target(*self._args, **self._kwargs)File "D:\hutao\projects\clear_market_data\test.py", line 6, in funcy = 100/(x-5)
ZeroDivisionError: division by zeroProcess finished with exit code 0

可以看到只有线程6报错了,但是其它线程和主进程是正常进行的

再次查看test.txt内容

0
1
2
3
4
6
7
8
9

可以看到,内容仅仅没有线程6的结果,说明子线程异常不会影响主线程

例子2

示例代码

import threadingdef func(x):y = 100/(x-5)data_list.append(x)if __name__ == "__main__":data_list = []th_list = []for i in range(10):th = threading.Thread(target=func, args=(i,))th.start()th_list.append(th)for th in th_list:th.join()print(data_list)

运行结果

D:\Python3.6.6\python.exe D:\hutao\projects\grid_trade\test.py 
Exception in thread Thread-6:
Traceback (most recent call last):File "D:\Python3.6.6\lib\threading.py", line 916, in _bootstrap_innerself.run()File "D:\Python3.6.6\lib\threading.py", line 864, in runself._target(*self._args, **self._kwargs)File "D:\hutao\projects\grid_trade\test.py", line 5, in funcy = 100/(x-5)
ZeroDivisionError: division by zero[0, 1, 2, 3, 4, 6, 7, 8, 9]Process finished with exit code 0

设计实现python主线程捕获子线程异常

需求

python多线程时,子线程出现异常,主线程依然正常向下执行,显然不符合工程代码,为解决子线程异常问题,提出研究内容:捕获子线程异常。

捕获子线程异常的好处是:

  • 程序在在多线程处理多个任务时,能够知道每个任务是否正常执行完毕,能够知道是哪个任务出了问题。不然即使子线程出了问题,但整个程序完毕,都不知道出了问题。
  • 在出了问题后,需要快速定位到是哪个地方出了问题。

问题

直接看代码:

def thread_text(i):time.sleep(i)raise Exception(u'error')def main():t = threading.Thread(target=thread_text, args=(1,))t.start()t.join()print(u'end')if __name__ =='__main__':main()

这是一个简单的测试,在子线程中返回一个异常,输出如下:

endException in thread Thread-1:
Traceback (most recent call last):File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_innerself.run()File "/usr/lib/python3.6/threading.py", line 864, in runself._target(*self._args, **self._kwargs)File "/usercode/file.py", line 8, in thread_textraise Exception(u'error')
Exception: error

可以看到,虽然子线程报出了异常,但main方法依然正常打印出了‘end’,这显然不是我们预期的,接下来,我们开始解决

解决思路

这里用的是queue来实现,它是python3的内置模块,创建堆栈队列,用来处理多线程通信。

queue用法参考:python内置模块之queue(队列)用法-CSDN博客

思路为:当子线程异常,则向queue添加一条数据,可以是任何数据,main方法检测queue是否为空,如果是空的,说明子线程无异常,反之则异常。然后通过检测活跃的线程数来确定子线程是否执行完毕。

更进一步的,添加的那条数据最好是携带更多的信息,比如具体是哪个线程,具体是什么异常信息,以及其它自定义的信息。比如,对于单个任务而言,可能会经过很多步骤,我们需要明确是在哪一步报了错,而且更重要的是如果该任务整体用时非常长,对于报错的任务,是不需要从头开始重新执行的。因此可以设计两个队列变量,一个用来记录错误信息,A,一个用来记录运行过程,B,比如完成一个任务需要10步,每成功运行一步就往B添加一条信息,在某一步报错时就往A添加一条信息。通过查看A和B内的数据,就可以明确是在哪一步报了错,然后检查代码bug等,以及对报错了的线程以及根据A和B中记录的信息来单独处理报错,而不是重新运行那个执行多线程任务的程序。

示例代码

检测活跃的线程数代码

threading.enumerate()  # list 当前活跃的线程,包含主线程
threading.active_count()  # int 当前活跃线程数,包含主线程

输出如下

[<_MainThread(MainThread, started 8628338176)>]
1

好了,思路及用到的模块都介绍完毕,直接上代码

import queue
import threading
import sys
import timedef thread_text(test_queue, i):try:time.sleep(i)raise Exception(u'test_error')except:error_info = sys.exc_info()# error_info = "......"test_queue.put(error_info)if __name__ == '__main__':q = queue.Queue()t = threading.Thread(target=thread_text, args=(q, 1))t.start()while True:if q.empty():if threading.active_count() == 0:breakelse:x = q.get()print(f"({x})")  raise Exception(u'main error')print(u'end')

输出

(<class 'Exception'>, Exception('test_error',), <traceback object at 0x00000256790AA8C8>)
Traceback (most recent call last):File "D:\BaiduSyncdisk\py_project\zs_market\codeList\test.py", line 26, in <module>raise Exception(u'main error')
Exception: main errorProcess finished with exit code 1

可以看到,并没有输出‘end’, 而是返回‘main error’, 说明主线程成功捕获到子线程,这里我是在子线程里queue.put了sys模块的exc_info(),能够更方便查错改错,当然可以是自定义的其它信息,根据需求来。


end

这篇关于python主线程捕获子线程异常的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062145

相关文章

使用Python将PDF表格自动提取并写入Word文档表格

《使用Python将PDF表格自动提取并写入Word文档表格》在实际办公与数据处理场景中,PDF文件里的表格往往无法直接复制到Word中,本文将介绍如何使用Python从PDF文件中提取表格数据,并将... 目录引言1. 加载 PDF 文件并准备 Word 文档2. 提取 PDF 表格并创建 Word 表格

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA

Python列表的创建与删除的操作指南

《Python列表的创建与删除的操作指南》列表(list)是Python中最常用、最灵活的内置数据结构之一,它支持动态扩容、混合类型、嵌套结构,几乎无处不在,但你真的会创建和删除列表吗,本文给大家介绍... 目录一、前言二、列表的创建方式1. 字面量语法(最常用)2. 使用list()构造器3. 列表推导式

Python使用Matplotlib和Seaborn绘制常用图表的技巧

《Python使用Matplotlib和Seaborn绘制常用图表的技巧》Python作为数据科学领域的明星语言,拥有强大且丰富的可视化库,其中最著名的莫过于Matplotlib和Seaborn,本篇... 目录1. 引言:数据可视化的力量2. 前置知识与环境准备2.1. 必备知识2.2. 安装所需库2.3

Python数据验证神器Pydantic库的使用和实践中的避坑指南

《Python数据验证神器Pydantic库的使用和实践中的避坑指南》Pydantic是一个用于数据验证和设置的库,可以显著简化API接口开发,文章通过一个实际案例,展示了Pydantic如何在生产环... 目录1️⃣ 崩溃时刻:当你的API接口又双叒崩了!2️⃣ 神兵天降:3行代码解决验证难题3️⃣ 深度

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处

python中的flask_sqlalchemy的使用及示例详解

《python中的flask_sqlalchemy的使用及示例详解》文章主要介绍了在使用SQLAlchemy创建模型实例时,通过元类动态创建实例的方式,并说明了如何在实例化时执行__init__方法,... 目录@orm.reconstructorSQLAlchemy的回滚关联其他模型数据库基本操作将数据添

Python实现快速扫描目标主机的开放端口和服务

《Python实现快速扫描目标主机的开放端口和服务》这篇文章主要为大家详细介绍了如何使用Python编写一个功能强大的端口扫描器脚本,实现快速扫描目标主机的开放端口和服务,感兴趣的小伙伴可以了解下... 目录功能介绍场景应用1. 网络安全审计2. 系统管理维护3. 网络故障排查4. 合规性检查报错处理1.

Go异常处理、泛型和文件操作实例代码

《Go异常处理、泛型和文件操作实例代码》Go语言的异常处理机制与传统的面向对象语言(如Java、C#)所使用的try-catch结构有所不同,它采用了自己独特的设计理念和方法,:本文主要介绍Go异... 目录一:异常处理常见的异常处理向上抛中断程序恢复程序二:泛型泛型函数泛型结构体泛型切片泛型 map三:文

JAVA线程的周期及调度机制详解

《JAVA线程的周期及调度机制详解》Java线程的生命周期包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED,线程调度依赖操作系统,采用抢占... 目录Java线程的生命周期线程状态转换示例代码JAVA线程调度机制优先级设置示例注意事项JAVA线程