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),解决多线程数据安全与协同问题,让并发编程更稳定、更安全!

相关推荐
十五年专注C++开发5 分钟前
浅谈LLVM
开发语言·c++·qt·clang·llvm
段一凡-华北理工大学5 分钟前
【高炉炼铁领域炉温监测、预警、调控智能体设计与应用】~系列文章10:实时预警机制:跑在问题前面!
网络·人工智能·python·知识图谱·高炉炼铁·工业智能体
小熊Coding23 分钟前
童年游戏冒险岛(Python版本)
python·游戏·pygame
白夜111729 分钟前
C++(标签派发 Tag Dispatching)
开发语言·c++·笔记·算法
WJ.Polar42 分钟前
Scapy基本应用
linux·运维·网络·python
@insist1231 小时前
信息安全工程师-入侵检测核心技术、APT 应对与工程实践
网络·安全·软考·信息安全工程师·软件水平考试
CSCN新手听安1 小时前
【Qt】Qt窗口(六)QMessageBox消息对话框的使用
开发语言·c++·qt
H_unique1 小时前
LangChain:调用工具Ⅲ
python·langchain
醉舞经阁半卷书12 小时前
深入掌握LangChain
python·langchain
CDN3602 小时前
[硬核] 你的DNS正在“裸奔”?用Python手撕DNS劫持与隧道检测逻辑
开发语言·网络·python