Python 多线程编程从入门到精通:原理+实战+最佳实践

🔥 Python 多线程编程从入门到精通:原理+实战+最佳实践

  • [一、先搞懂核心:进程 vs 线程 🧩](#一、先搞懂核心:进程 vs 线程 🧩)
    • [1. 核心定义与关系](#1. 核心定义与关系)
    • [2. 性能关键:IO 场景下的优势](#2. 性能关键:IO 场景下的优势)
    • [3. 进程与线程关系可视化](#3. 进程与线程关系可视化)
  • [二、多线程为什么快?爬虫场景秒懂 ⚡️](#二、多线程为什么快?爬虫场景秒懂 ⚡️)
    • [1. 串行 vs 多线程 对比](#1. 串行 vs 多线程 对比)
    • [2. GIL 与线程切换原理](#2. GIL 与线程切换原理)
  • [三、实战第一种:Thread 类直接实例化 ✍️](#三、实战第一种:Thread 类直接实例化 ✍️)
    • [1. 核心步骤](#1. 核心步骤)
    • [2. 完整实战代码](#2. 完整实战代码)
    • [3. 关键机制:守护线程 `setDaemon()`](#3. 关键机制:守护线程 setDaemon())
    • [4. 关键机制:阻塞等待 `join()`](#4. 关键机制:阻塞等待 join())
  • [四、实战第二种:继承 Thread 类 🎯](#四、实战第二种:继承 Thread 类 🎯)
    • [1. 核心规则](#1. 核心规则)
    • [2. 完整实战代码](#2. 完整实战代码)
    • [3. 两种写法适用场景对比](#3. 两种写法适用场景对比)
  • [五、多线程核心知识点总结 📌](#五、多线程核心知识点总结 📌)
  • 六、下期预告

在 python 开发中,并发编程是提升程序效率、优化资源利用率的核心技能,而多线程作为并发编程的基础方案,几乎贯穿所有 IO 密集型场景。从爬虫提速到接口并发处理,多线程都能以轻量、高效的方式,让程序告别低效串行,真正实现"同时干活"。

本文将从零拆解 python 多线程核心原理,手把手实现两种主流编码方式,详解守护线程与 join() 阻塞机制,搭配实战代码与可视化逻辑,帮你彻底吃透多线程!


一、先搞懂核心:进程 vs 线程 🧩

很多初学者会混淆进程线程,这是理解多线程的第一步,先理清两者的关系与定位:

1. 核心定义与关系

  • 进程:操作系统资源分配的最小单元,独立占有内存、文件句柄等系统资源,体积大、调度成本高。

  • 线程:操作系统调度执行的最小单元依附于进程存在,共享进程资源,调度轻量、切换更快。

简单理解:进程是容器,线程是干活的工人,一个容器里可以有多个工人协同工作。

2. 性能关键:IO 场景下的优势

对于IO 密集型程序(网络请求、文件读写、数据库查询):

✅ 多线程 ≈ 多进程 性能

✅ 线程调度更轻量,系统开销远小于进程

✅ 遇到 IO 阻塞时,GIL 自动释放,线程无缝切换

这也是 python 多线程最适合的场景!

3. 进程与线程关系可视化

操作系统
进程1

资源独立
进程2

资源独立
线程1

共享进程资源
线程2

共享进程资源
线程3

共享进程资源
线程1

共享进程资源

图表说明:进程相互独立,线程隶属于进程并共享资源,操作系统直接调度线程执行。


二、多线程为什么快?爬虫场景秒懂 ⚡️

以最经典的爬虫场景举例,瞬间理解多线程的并发价值:

1. 串行 vs 多线程 对比

  • 串行执行:先爬列表页 → 等待网络返回 → 再爬详情页 → 等待网络返回,等待时间双倍消耗

  • 多线程执行:线程1爬列表页、线程2爬详情页,IO 阻塞时线程切换,等待时间被充分利用,总耗时≈最长单线程耗时。

2. GIL 与线程切换原理

python 的 GIL(全局解释器锁)不会影响 IO 密集型场景的并发效率:

当线程执行网络请求(Socket IO) 时,GIL 自动释放,另一个线程立即接管执行,实现伪并发,效率远超串行。


三、实战第一种:Thread 类直接实例化 ✍️

这是 python 多线程最简写法,适合逻辑简单、快速实现的场景。

1. 核心步骤

  1. 导入 threading 模块

  2. 定义线程执行的函数

  3. 实例化 Thread,指定 target(执行函数)和 args(参数)

  4. 调用 start() 启动线程

2. 完整实战代码

python 复制代码
import threading
import time

# 模拟爬取详情页
def get_detail_html(url):
    print(f"✅ get_detail_html started:{url}")
    time.sleep(2)  # 模拟网络IO等待
    print(f"✅ get_detail_html end:{url}")

# 模拟爬取列表页URL
def get_detail_url(url):
    print(f"✅ get_detail_url started:{url}")
    time.sleep(4)  # 模拟网络IO等待
    print(f"✅ get_detail_url end:{url}")

if __name__ == "__main__":
    start_time = time.time()
    
    # 实例化线程
    t1 = threading.Thread(target=get_detail_html, args=("https://xxx.com/1",))
    t2 = threading.Thread(target=get_detail_url, args=("https://xxx.com",))
    
    # 启动线程
    t1.start()
    t2.start()
    
    # 阻塞主线程,等待子线程执行完毕
    t1.join()
    t2.join()
    
    print(f"⏱ 总耗时:{time.time() - start_time} s")

3. 关键机制:守护线程 setDaemon()

  • 作用:将线程设为守护线程,主线程退出时,守护线程直接被终止,不等待执行完毕。

  • 用法:t.setDaemon(True)(必须在 start() 前设置)

4. 关键机制:阻塞等待 join()

  • 作用:让主线程阻塞等待子线程执行完成,再继续后续逻辑。

  • 效果:总耗时 = 最长子线程耗时(示例中为4秒),证明并发执行。


四、实战第二种:继承 Thread 类 🎯

当业务逻辑复杂、需要封装多个方法时,继承 Thread 类是更优雅的方案,符合面向对象设计。

1. 核心规则

  1. 自定义类继承 threading.Thread

  2. 必须重写 ** run() ** 方法(线程执行逻辑)

  3. 不要重写 start() 方法

  4. 可重写 __init__ 传递自定义参数

2. 完整实战代码

python 复制代码
import threading
import time

# 继承Thread类,封装详情页爬取
class GetDetailHtml(threading.Thread):
    def __init__(self, name, url):
        super().__init__(name=name)  # 调用父类构造
        self.url = url
    
    def run(self):
        print(f"✅ {self.name} started:{self.url}")
        time.sleep(2)
        print(f"✅ {self.name} end:{self.url}")

# 继承Thread类,封装列表页爬取
class GetDetailUrl(threading.Thread):
    def __init__(self, name, url):
        super().__init__(name=name)
        self.url = url
    
    def run(self):
        print(f"✅ {self.name} started:{self.url}")
        time.sleep(4)
        print(f"✅ {self.name} end:{self.url}")

if __name__ == "__main__":
    start_time = time.time()
    
    t1 = GetDetailHtml(name="detail_thread", url="https://xxx.com/1")
    t2 = GetDetailUrl(name="url_thread", url="https://xxx.com")
    
    t1.start()
    t2.start()
    
    t1.join()
    t2.join()
    
    print(f"⏱ 总耗时:{time.time() - start_time} s")

3. 两种写法适用场景对比

写法 优点 适用场景
直接实例化 Thread 代码简洁、快速编写 逻辑简单、动态创建线程、线程池
继承 Thread 类 封装性强、可扩展、易维护 逻辑复杂、需要自定义属性/方法

五、多线程核心知识点总结 📌

  1. 线程是进程的子集,是 OS 调度最小单元,IO 场景下轻量高效。

  2. 两种实现方式 :直接实例化 / 继承 Thread 重写 run()

  3. 守护线程setDaemon(True) → 主线程退出,守护线程终止。

  4. 阻塞等待join() → 主线程等待子线程完成,保证逻辑完整。

  5. IO 密集型优先用多线程,CPU 密集型推荐多进程。


六、下期预告

下一篇将深入讲解线程间通信(Queue、Event、Lock),解决多线程数据安全与协同问题,让并发编程更稳定、更安全!

相关推荐
feng_you_ying_li2 小时前
C++11,lambda,包装器
开发语言·数据结构·c++
郝学胜-神的一滴2 小时前
深度学习激活函数核心精讲:Sigmoid 原理、推导与工程实践
人工智能·pytorch·python·深度学习·神经网络·机器学习
sycmancia2 小时前
Qt——布局管理区(二)
开发语言·前端·qt
好家伙VCC2 小时前
**TEE在嵌入式安全中的应用实践:基于ARM TrustZone的加密存储方案设计与实现*
java·arm开发·python·struts·安全
傻啦嘿哟2 小时前
Python 操作 Word 页眉页脚完整指南
开发语言·c#
亚空间仓鼠2 小时前
Python学习日志(二):基础语法
windows·python·学习
阿kun要赚马内2 小时前
Python装饰器的原理详解
开发语言·python
kyle~2 小时前
FANUC机械臂---R寄存器
开发语言·c++·机器人·fanuc
TechWayfarer2 小时前
跨境电商IP归属地API实战:如何用IP纯净度检测避开连坐封号?
网络·网络协议·tcp/ip