Python多线程入门教程
前言学 Python 多线程第一步要先把预期摆正多线程不是让程序算得更快的万能药。它擅长的是「一边等、一边干活」——比如同时下载几十个页面、同时读写多个文件。如果你的瓶颈是一大堆计算多线程帮不上忙原因在 GIL见下文第四节。新手最常见的两个错误认知「开 8 个线程就等于 8 核并行」——在默认的 CPython 里不是这样。「线程越多越快」——线程一多锁竞争和上下文切换的开销反而吃掉收益。这篇教程从零开始先写出第一个多线程程序再讲清线程的创建、等待、参数传递然后补上绕不开的同步问题最后给一个可以照抄的线程池示例。示例以 CPython 3.8 及以上为基准。一、第一个多线程程序不使用任何库标准库自带的threading就够了。# 适用于 Python 3.8import threadingimport timedef say_hello(name):print(f线程 {name} 开始)time.sleep(1) # 模拟一件需要等待的事print(f线程 {name} 结束)t1 threading.Thread(targetsay_hello, args(A,))t2 threading.Thread(targetsay_hello, args(B,))t1.start() # 启动立刻返回t2.start()t1.join() # 主线程在这里等 t1 结束t2.join()print(主线程结束)运行这段代码会先看到两个「开始」大约 1 秒后再看到两个「结束」最后才是「主线程结束」。总耗时大约 1 秒而不是 2 秒——因为两次sleep是重叠发生的。这就是并发带来的收益。如果改成先t1.join()再t2.start()两次等待就会串起来耗时翻倍。start()要先把所有线程都点着最后统一join()这是新手最容易写反的地方。二、Thread的构造参数threading.Thread的签名来自官方文档Thread(groupNone, targetNone, nameNone, args(), kwargs{}, *, daemonNone, contextNone)常用的几个参数参数含义备注target线程要执行的可调用对象不传就什么也不做args传给target的位置参数必须是元组单元素别忘逗号kwargs传给target的关键字参数字典name线程名仅供标识省略时自动生成daemon是否为守护线程必须在start()前确定一个高频踩坑点args是元组只传一个参数时必须写成args(A,)逗号不能丢。写成args(A)只是字符串Thread会把它按字符拆开当成多个参数——或者干脆报参数个数不对。# 适用于 Python 3.8import threadingdef show(a, b):print(fa{a!r}, b{b!r})# 正确元组里带逗号threading.Thread(targetshow, args(1, 2)).start()# 正确传关键字参数threading.Thread(targetshow, kwargs{a: 1, b: 2}).start()三、两种创建方式方式一函数式。把现成的函数交给target最省事也最推荐。# 适用于 Python 3.8import threadingdef task(n):print(f处理 {n})threading.Thread(targettask, args(5,)).start()方式二继承式。定义一个类继承threading.Thread重写run()。# 适用于 Python 3.8import threadingclass MyTask(threading.Thread):def __init__(self, n, **kwargs):super().__init__(**kwargs) # 必须先调用父类构造self.n ndef run(self): # 线程体写在这里print(f处理 {self.n}线程名 {self.name})t MyTask(5)t.start() # start() 内部会调用 run()官方文档有一句关键提醒继承Thread时只应重写__init__()和run()不要重写其他方法。并且要直接调用t.start()而不是t.run()——直接调run()只是在当前线程里跑一个普通函数根本没有新线程。四、绕不开的 GILCPython 里有一把全局解释器锁GILGlobal Interpreter Lock。官方文档的说法是同一时刻只有一个线程能执行 Python 代码。由此得到两条实用结论I/O 密集任务用线程有效线程在等网络、等磁盘时会释放 GIL别的线程能接着跑。CPU 密集任务用线程无效大家抢同一把锁多核用不上官方建议改用multiprocessing或concurrent.futures.ProcessPoolExecutor。补充一句事实CPython 3.13 起有实验性的自由线程no-GIL构建可以真正并行跑线程但它不是默认构建、需要单独安装所以日常说到「Python 的多线程」时GIL 依然存在。五、共享数据与锁线程共享同一个进程的内存多个线程同时改一个变量就会出问题。# 适用于 Python 3.8import threadingcount 0def increase():global countfor _ in range(100_000):count 1 # 读-改-写三步之间可能被切换ts [threading.Thread(targetincrease) for _ in range(4)]for t in ts:t.start()for t in ts:t.join()print(count) # 期望 400000实际通常偏小原因count 1在字节码层面是「取值 → 加一 → 写回」三步解释器会在三步之间切换线程两个线程可能都读到同一个旧值各自加一后写回于是丢了一次累加。用threading.Lock把这三步锁成一个整体# 适用于 Python 3.8import threadingcount 0lock threading.Lock()def increase():global countfor _ in range(100_000):with lock: # 自动 acquire / release异常也会释放count 1要点锁保护的是「不可分割的一整段操作」不是单个变量。加锁范围过大会让线程排队等于变回单线程范围过小则保护不住。如果只是多个线程往一个地方塞数据、再由别人取走更省心的做法是用queue.Queue——它是线程安全的内部自带锁。六、完整示例线程池批量探测手工start()/join()一批线程写起来啰嗦且容易漏。生产代码更常用concurrent.futures.ThreadPoolExecutor。# 适用于 Python 3.8import concurrent.futuresimport timedef probe(name, delay):time.sleep(delay) # 模拟一次耗时的 I/Oreturn f{name} 完成用时 {delay}sdef main():jobs [(任务1, 1.0), (任务2, 1.2), (任务3, 0.4)]with concurrent.futures.ThreadPoolExecutor(max_workers3) as pool:futures [pool.submit(probe, name, d) for name, d in jobs]for fut in concurrent.futures.as_completed(futures):print(fut.result())if __name__ __main__:main()三个任务串行要 2.6 秒左右用线程池并发后总耗时由最慢的那个决定约 1.2 秒。with块退出时会自动shutdown(waitTrue)不必手动收尾。map()与submit()的区别值得记一下submit()一次提交一个任务、返回Futuremap()一次提交一批、按提交顺序返回结果迭代器。需求用法提交单个任务、逐个取结果pool.submit(fn, *args)一批任务、只要最终结果pool.map(fn, iterable)一批任务、按完成先后处理submitas_completed需要限制并发数构造时给max_workers赋值常见坑点1. 先join()再start()写成串行❌t1.start(); t1.join(); t2.start(); t2.join()✅t1.start(); t2.start(); t1.join(); t2.join()—— 先全部启动再统一等待。2. 单参数忘了逗号❌Thread(targetf, args(abc))abc被当作三个字符拆开传。 ✅args(abc,)或args[abc]。3. 调用run()而不是start()❌t.run()—— 在当前线程里同步执行白写了。 ✅t.start()由解释器另开线程调用run()。4. 对同一个线程对象重复start()❌t.start(); t.join(); t.start()抛RuntimeError。 ✅ 一个线程对象只能启动一次要重跑请新建对象。5. 用setDaemon()设置守护线程❌t.setDaemon(True)3.10 起已废弃。 ✅Thread(targetf, daemonTrue)或start()之前写t.daemon True。6. 主线程没join()就读结果❌for t in ts: t.start()之后马上print(count)。 ✅ 先对所有线程join()或用ThreadPoolExecutor的with块。7. 用全局列表直接 append 之外的结构做共享❌ 用普通字典一边读一边删抛RuntimeError: dictionary changed size during iteration。 ✅ 用queue.Queue传递数据或全程with lock:保护读改。8. 在 CPU 密集任务上堆线程❌ 用 8 个线程做纯计算结果和单线程一样慢、甚至更慢。 ✅ 纯计算任务改用ProcessPoolExecutor线程只留给 I/O。总结主题关键结论创建线程Thread(target..., args(...))别忘了start()等待线程join()阻塞直到结束恒返回None并发收益只体现在 I/O 密集任务上GIL默认 CPython 同一时刻只跑一个线程的字节码共享数据必须加锁或改用queue.Queue批量任务优先ThreadPoolExecutor别再手工管理一堆线程入门阶段的正确顺序是先学会「启动 等待」这两个动作再理解「为什么需要锁」最后形成「I/O 用线程、计算用进程」的直觉。把线程当成 I/O 的工具而不是提速的魔法就不会走进死胡同。

相关新闻

TensorFlow工程化实战:从自定义训练循环到跨平台部署的完整链路

TensorFlow工程化实战:从自定义训练循环到跨平台部署的完整链路

1. 为什么“tensorflow”这个标题值得单独拎出来聊如果你在搜索引擎里敲下“tensorflow”这个词,跳出来的结果大概率是官方文档、API手册、安装教程三件套。但真正在一线写过模型、调过参、部署过服务的人都知道,这个标题背后藏着的不是一个库那么简单&a…

2026/10/13 7:12:57 阅读更多 →
智音技术OED驱动修复指南:解决电脑无声与设备管理器代码28/31

智音技术OED驱动修复指南:解决电脑无声与设备管理器代码28/31

简介:面向华为与英特尔智音技术OED音频驱动更新失败、设备管理器出现黄色感叹号的用户,此修复包提供了完整的离线驱动和安装配置所需文件,适合需要手动重装或排查音频设备冲突的初级、中级维护者,也适合离线环境或自动更新反复失败…

2026/10/13 7:11:57 阅读更多 →
智音技术OED驱动修复指南:从设备管理器异常到脚本化排查

智音技术OED驱动修复指南:从设备管理器异常到脚本化排查

简介:华为英特尔智音技术OED驱动修复包,面向因驱动更新失败而在设备管理器中看到黄色感叹号、音频功能无法正常工作的Windows用户。压缩包共收录598个文件,整体约326.72MB,既包含inf配置、dll动态库、sys系统驱动、cat安全编录等常…

2026/10/13 7:11:57 阅读更多 →

最新新闻

SQL UNIQUE约束详解:原理、数据库差异与防重复设计实践

SQL UNIQUE约束详解:原理、数据库差异与防重复设计实践

1. UNIQUE约束到底是什么,为什么你需要它做数据库设计这些年,我见过太多因为“忘记加唯一约束”而崩溃的业务系统。最典型的场景:用户注册接口被脚本刷了一遍,username重复了;订单表里同一笔订单被回调程序insert了两次…

2026/10/13 7:55:19 阅读更多 →
Agent-Skills:智能体原子化能力的设计与工程实践

Agent-Skills:智能体原子化能力的设计与工程实践

1. “agent-skills”不是新词,而是智能体能力体系的命名共识 最近在多个技术社区、开源项目文档和内部架构评审会上,“agent-skills”这个组合词出现频率陡增——它既不指代某个具体开源库,也不是某家公司的私有协议,而是一类设计…

2026/10/13 7:55:19 阅读更多 →
Flink DataStream与Table API转换实战:Schema推导、时间属性与常见避坑指南

Flink DataStream与Table API转换实战:Schema推导、时间属性与常见避坑指南

做实时数据处理这几年,Flink的DataStream和Table API来回切换是我用得最频繁的操作之一。刚接触那会儿,总觉得这两套API是两套独立的体系,流上处理业务逻辑用DataStream,做统计查询用Table,中间隔着一道说不清的墙。后…

2026/10/13 7:55:19 阅读更多 →
LiteLLM:大模型API统一网关与智能路由实战指南

LiteLLM:大模型API统一网关与智能路由实战指南

1. LiteLLM不是另一个大模型,而是一把“万能适配器”最近在几个技术群和开源社区里,频繁看到有人发问:“LiteLLM到底是个啥?是又一个新模型吗?”“它和Ollama、LM Studio有什么区别?”——这种误解非常典型…

2026/10/13 7:55:19 阅读更多 →
LiteLLM:大模型API统一接入与智能路由实战指南

LiteLLM:大模型API统一接入与智能路由实战指南

1. 为什么“litellm”突然在开发者圈里被高频提及——它不是新模型,而是新范式最近两周,在多个技术社区、内部分享会和开源项目讨论区里,“litellm”这个词出现的频率明显升高。它不像Llama、Qwen或Claude那样代表一个具体的大语言模型&#…

2026/10/13 7:55:19 阅读更多 →
开源代码审查实战:流程设计、工具配置与团队落地方法

开源代码审查实战:流程设计、工具配置与团队落地方法

代码审查这事儿,我过去几年一直想好好聊一聊。初看上去,open-code-review 这个项目标题没什么特别,好像就是把自己团队做代码评审的过程、工具和规范整理出来,开源给社区用。但如果真这么理解,那就有点浪费了。代码审查…

2026/10/13 7:54:17 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →