Python线程编程之Thread详解
前言threading.Thread是多线程编程的入口类但很多人只用了它的start()和join()对构造函数里那几个参数、run()与start()的分工、ident与native_id的区别、异常到底去了哪里都是模糊的。结果是遇到「线程静默退出」「异常看不见」「线程名对不上」这类问题时无从下手。本文把Thread这一个类拆开讲透构造签名、两种指定任务的方式、全部属性与方法、异常处理钩子以及threading模块级的几个辅助函数。所有签名均以 Python 官方文档为准示例以 CPython 3.8 及以上为基准涉及更高版本的改动会单独标注。一、构造签名与参数官方文档给出的签名是threading.Thread(groupNone, targetNone, nameNone, args(), kwargs{}, *, daemonNone, contextNone)逐个参数说清楚参数默认值说明groupNone保留参数必须为NonetargetNone线程要执行的可调用对象None表示什么也不做nameNone线程名省略时自动生成args()传给target的位置参数元组kwargs{}传给target的关键字参数字典daemonNone是否守护线程None表示继承当前线程contextNone上下文对象Python 3.14 新增不必手动传文档提醒这个构造函数应当始终用关键字参数调用也就是写Thread(targetf, args(1,))不要图省事按位置去传group。关于name省略时默认按Thread-N形式生成从 3.10 起如果指定了target还会把 target 的函数名并进去形如Thread-N (target)。另外要记住名字只是个标识允许重复没有任何语义。关于daemon初值继承自创建它的线程主线程不是守护线程所以在主线程里创建出来的线程默认都是daemonFalse。二、run()与start()的分工这是全篇最该记牢的一组关系start()只能调用一次负责真的开一个操作系统线程并让新线程去执行run()。重复调用抛RuntimeError。run()方法体才是任务本身。默认实现会去调用构造时传入的target。于是有两种指定任务的方式# 适用于 Python 3.8import threadingimport timedef work(n):time.sleep(0.1)print(f[target 方式] 处理 {n})# 方式一传 targett1 threading.Thread(targetwork, args(1,), namevia-target)t1.start()# 方式二继承并重写 runclass Worker(threading.Thread):def __init__(self, n):super().__init__(namevia-subclass)self.n ndef run(self):time.sleep(0.1)print(f[subclass 方式] 处理 {self.n})t2 Worker(2)t2.start()t1.join()t2.join()官方文档明确子类里只应重写__init__()和run()其他方法都不要覆盖重写__init__()时第一件事必须是调用父类构造。还有一个高频错误是直接调t.run()——那只是在当前线程里把一个方法跑一遍压根没有新线程。run()里抛出的未捕获异常不会让主线程崩溃它会交给threading.excepthook()处理。三、属性与方法清单成员类型说明start()方法启动线程每个对象至多一次run()方法线程体可重写join(timeoutNone)方法阻塞等待结束恒返回Noneis_alive()方法是否存活run()开始前到结束后这段时间为Truename属性线程名仅作标识daemon属性布尔值必须在start()前设置ident属性线程标识符未启动时为Nonenative_id属性OS 分配的线程 ID3.8 新增isDaemon()/setDaemon()方法自 3.10 起废弃改用daemongetName()/setName()方法自 3.10 起废弃改用nameident和native_id的区别值得单独说。ident是 Python 层的线程标识是非零整数线程退出后这个值仍可读取且可能被回收给后来创建的线程。native_id是操作系统内核分配的线程 ID非负整数线程没启动时是None在系统范围内唯一直到线程结束。两者都不能当作全局唯一键长期保存。join()有三个容易记错的点永远返回None别指望它把结果带回来。想知道是正常结束还是超时得在join(timeout)之后再查is_alive()。对当前线程自己调join()会抛RuntimeError那是自锁死。对还没start()的线程调join()也抛RuntimeError。四、异常去了哪里threading.excepthookrun()里未捕获的异常不会像主线程那样打印完整的调用方栈并终止程序它会走一个专门的钩子。默认实现会忽略SystemExit并把其他异常打印到标准错误。可以自己接管这个钩子做统一日志# 适用于 Python 3.8import threadingimport tracebackdef my_hook(args):# args 有 exc_type / exc_value / exc_traceback / thread 四个属性print(f线程 {args.thread.name} 出错了, flushTrue)traceback.print_exception(args.exc_type, args.exc_value, args.exc_traceback)threading.excepthook my_hookdef boom():raise ValueError(线程里的意外错误)t threading.Thread(targetboom, nameboom-thread)t.start()t.join()print(主线程继续执行没有被异常带走)args的四个属性exc_type、exc_value、exc_traceback、thread来自官方文档定义的ExceptHookArgs。注意异常被这个钩子吞掉了调用方不会收到所以用了线程池时更要通过Future.result()把异常取回来。五、模块级辅助函数与local除了Thread类threading模块还提供函数作用threading.current_thread()返回当前线程对象threading.main_thread()返回主线程对象threading.enumerate()返回所有存活线程的列表含守护线程threading.active_count()存活线程数量等于enumerate()的长度threading.get_ident()当前线程的identthreading.get_native_id()当前线程的native_idthreading.local()线程本地存储threading.local()解决的是「每个线程各有一份同名变量」的问题。它的属性在不同线程里互不干扰# 适用于 Python 3.8import threadingimport timedata threading.local()def process(user):data.user user # 只写进本线程自己的那份time.sleep(0.1)print(f线程 {threading.current_thread().name} 看到 user {data.user})ts [threading.Thread(targetprocess, args(fu{i},), nameft{i}) for i in range(3)]for t in ts:t.start()for t in ts:t.join()如果data换成普通全局变量三个线程会互相覆盖用threading.local()之后每个线程读到的都是自己写进去的那份。数据库连接、请求上下文这类「每个线程一份」的对象正是它的典型用途。六、一个综合示例# 适用于 Python 3.8import threadingimport timedef worker(lock, results, idx):with lock:results.append((idx, threading.current_thread().name,threading.get_ident()))time.sleep(0.05)lock threading.Lock()results []threads [threading.Thread(targetworker, args(lock, results, i),namefworker-{i}) for i in range(4)]for t in threads:t.start()for t in threads:t.join()for item in sorted(results):print(item)print(活跃线程数不含已结束的, threading.active_count())print(主线程, threading.main_thread().name)这里用lock保护results.append因为列表的追加虽然通常安全但读改写在并发下仍应显式同步。active_count()在join()之后只会剩下主线程自己。常见坑点1. 直接调用run()而不调用start()❌t.run()—— 在当前线程里同步执行完全没有并发。 ✅t.start()。2. 对同一个线程对象重复start()❌t.start(); t.join(); t.start()抛RuntimeError。 ✅ 每个对象只能启动一次要重跑请新建对象。3.join()的返回值当成任务结果❌res t.join()然后print(res)永远得到None。 ✅ 结果通过共享容器、queue.Queue或Future.result()取回。4. 忘了join之后还要查is_alive❌t.join(1)之后默认线程一定已经结束。 ✅join返回None超时与否只能靠is_alive()判断。5. 在线程自己身上调join()❌ 线程里写threading.current_thread().join()——RuntimeError。 ✅join只能由别的线程调用。6. 还在用setDaemon()/getName()❌t.setDaemon(True)、t.getName()3.10 起会给DeprecationWarning。 ✅ 用t.daemon True必须在start()前和t.name。7. 把ident当全局唯一 ID 长期保存❌ 用t.ident做字典键缓存数据以为永不重复。 ✅ident会在线程结束后被回收复用需要稳定标识请自己生成uuid。8. 以为子线程异常会终止主程序❌ 指望子线程里抛异常能中断整个程序。 ✅ 未捕获异常交给threading.excepthook主程序照常需要感知异常就用线程池的Future.result()。总结成员关键结论Thread(...)用关键字参数args是元组、kwargs是字典start()只调用一次负责开线程run()线程体可重写不要直接调用join()恒返回None只等待不终止is_alive()判断是否存活配join(timeout)用ident/native_idPython 标识 / OS 标识都会被回收daemon必须在start()前设置excepthook接管子线程未捕获异常local()每个线程一份的本地存储把Thread的这些成员分清「谁负责启动、谁负责等待、谁负责传递结果、异常去了哪」多线程代码就不再是一团黑箱。下一步再学Lock、Event、Queue串起来就是一套完整可用的并发工具箱。

相关新闻

论文降重工具怎么选?五款实测对比与查重原理全解析

论文降重工具怎么选?五款实测对比与查重原理全解析

1. 先搞清楚查重系统怎么工作,再谈降重工具怎么选1.1 标红规则:连续多少字符一样会被判重论文降重大概是硕博阶段最容易被低估的一件苦差事。头一回把初稿提交到学校指定的查重平台,看到接近30%的重复率,很多人整个周末都在重写。…

2026/10/10 19:25:02 阅读更多 →
6大Lens质量评测集全解析:jacobian-lens如何科学量化“透镜“的好坏

6大Lens质量评测集全解析:jacobian-lens如何科学量化“透镜“的好坏

【免费下载链接】jacobian-lens Companion code for the global workspace interpretability paper 项目地址: https://gitcode.com/gh_mirrors/ja/jacobian-lens 点击查看 免费下载 你是否好奇语言模型内部到底在"想什么"?jacobian-lens&…

2026/10/10 19:25:02 阅读更多 →
NumPy核心概念与实战:从ndarray到广播机制指南

NumPy核心概念与实战:从ndarray到广播机制指南

很多朋友学 Python 科学计算,第一个绕不开的库就是 NumPy。就算你还没决定走数据分析还是人工智能方向,随便打开一本教程,目录里大概率都会出现“11.1 NumPy 简介与核心概念”这样的小节。这个标题听起来平平无奇,但真上手之后你会…

2026/10/10 19:25:02 阅读更多 →

最新新闻

Elasticsearch调优实战:JVM堆内存、分片规划与Windows避坑指南

Elasticsearch调优实战:JVM堆内存、分片规划与Windows避坑指南

说实话,Elasticsearch 这个调优话题,网上教程已经多到泛滥了。但你随便翻十篇帖子,八篇都在让你改indices.query.bool.max_clause_count、调refresh_interval,改完就扔给你一个“性能提升十倍”的结论。我见过太多人按这种教程改完…

2026/10/10 23:12:48 阅读更多 →
从“一个人整理分支“到“给 Agent 立法“:Worktrunk 爆火暴露了工作流工具的重心迁移

从“一个人整理分支“到“给 Agent 立法“:Worktrunk 爆火暴露了工作流工具的重心迁移

从"一个人整理分支"到"给 Agent 立法":Worktrunk 爆火暴露了工作流工具的重心迁移 【免费下载链接】worktrunk Worktrunk is a CLI for Git worktree management, designed for parallel AI agent workflows 项目地址: https://gitcode.com/G…

2026/10/10 23:12:48 阅读更多 →
蓝桥杯省赛真题怎么刷?第十六届真题解析与备赛策略

蓝桥杯省赛真题怎么刷?第十六届真题解析与备赛策略

每一届蓝桥杯省赛结束之后,都有大量同学来找我聊同一个问题:“真题到底该怎么刷?我现在开始备赛,是先做题库还是先做真题?”我的答案一直没变过:真题永远是效率最高的切入点。第十六届省赛题目我也在持续整…

2026/10/10 23:12:48 阅读更多 →
没有显卡也能跑:GLiNER2.5-Decide CPU 部署全流程,含批量请求与长文档处理

没有显卡也能跑:GLiNER2.5-Decide CPU 部署全流程,含批量请求与长文档处理

没有显卡也能跑:GLiNER2.5-Decide CPU 部署全流程,含批量请求与长文档处理 【免费下载链接】GLiNER2.5-Decide 项目地址: https://ai.gitcode.com/hf_mirrors/fastino/GLiNER2.5-Decide 当一个 340M 参数、基于 DeBERTa-v3-large 的英文分类模型…

2026/10/10 23:12:48 阅读更多 →
验证码识别全链路实战:从数据清洗到CRNN部署

验证码识别全链路实战:从数据清洗到CRNN部署

简介:本资源是一套面向深度学习初学者与图像识别实践者的字符型数字验证码识别完整实现方案,聚焦网络安全中验证码攻防场景下的模型训练与部署实战。资源包含1210个文件,主体为978张PNG与202张JPG格式的验证码样本图像,辅以17个核…

2026/10/10 23:11:47 阅读更多 →
AI助手自定义指令实战:从边界设定到规则调参

AI助手自定义指令实战:从边界设定到规则调参

1. 给 workbuddy 定规矩之前,先得想明白它到底是个什么角色我一开始用 workbuddy 的时候,状态基本是这样的:新鲜头两天觉得挺智能,让它查资料、整理纪要、排个日程都干得动,但越用越觉得哪里不对劲——它交出来的东西“…

2026/10/10 23:11:47 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →