Python3 线程:多任务并发编程详解
线程(Thread)是 Python 实现并发编程的重要方式,适合处理 I/O 密集型任务(如网络请求、文件读写)。Python3 通过 threading 模块提供了完善的线程支持,本文将详细介绍线程的创建、管理、同步及实战应用。
线程的基本概念
- 线程:操作系统调度的最小单位,属于进程的一部分,多个线程共享进程的内存空间。
- 并发:多个任务交替执行(单核 CPU 模拟多任务)。
- 并行:多个任务同时执行(多核 CPU 真正同时运行)。
- Python 线程特点:
- 受 GIL(全局解释器锁)限制,CPU 密集型任务多线程效率可能不如单线程;
- I/O 密集型任务(如网络请求、文件读写)能显著提升效率。
线程的创建与启动
Python3 中创建线程主要有两种方式:使用 threading.Thread 类和继承 threading.Thread 类重写 run() 方法。
方式一:直接使用 threading.Thread
通过 threading.Thread(target=函数名, args=参数) 创建线程对象,调用 start() 启动线程。
1 | import threading |
输出说明:两个线程交替执行,Thread-1 间隔 1 秒,Thread-2 间隔 2 秒,体现并发特性。
方式二:继承 threading.Thread 类
重写 run() 方法(线程执行的核心逻辑),通过 start() 启动。
1 | import threading |
线程的核心方法与属性
| 方法 / 属性 | 说明 |
|---|---|
start() |
启动线程,自动调用 run() 方法(不能重复调用) |
run() |
线程执行的核心逻辑,可重写 |
join([timeout]) |
阻塞当前线程,等待被调用线程执行完毕(timeout 为最大等待秒数) |
is_alive() |
判断线程是否在运行中 |
getName()/setName() |
获取 / 设置线程名称 |
ident |
线程标识符(整数),线程启动后有效,结束后为 None |
daemon |
布尔值,设置为 True 时为守护线程(主程序结束后自动退出,需在 start() 前设置) |
示例:守护线程(后台线程)
守护线程用于服务其他线程(如日志记录),主程序结束后自动退出,无需等待其完成。
1 | import threading |
输出:主程序结束后,守护线程的无限循环会被强制终止。
线程同步:解决资源竞争
多个线程共享全局变量时,可能因资源竞争导致数据不一致。需通过同步机制(如锁、信号量)保证操作的原子性。
1. 锁(threading.Lock)
最常用的同步方式,通过 acquire() 获取锁,release() 释放锁,确保同一时间只有一个线程执行临界区代码。
1 | import threading |
说明:count += 1 实际包含 “读取 - 修改 - 写入” 三步,不加锁时可能因线程交替执行导致数据错误,加锁后确保操作完整执行。
2. 可重入锁(threading.RLock)
允许同一线程多次获取锁(避免递归调用时死锁),需释放相同次数。
1 | import threading |
3. 其他同步工具
| 工具 | 用途 |
|---|---|
threading.Event |
线程间通信,通过 set() 发送信号,wait() 等待信号 |
threading.Semaphore |
控制同时访问资源的线程数量(如限制并发连接数) |
threading.Condition |
更灵活的条件同步,支持 wait()/notify()/notify_all() 机制 |
线程池:高效管理线程
频繁创建 / 销毁线程会消耗资源,线程池可预先创建固定数量的线程,重复利用以提高效率。Python3 通过 concurrent.futures.ThreadPoolExecutor 实现。
1 | from concurrent.futures import ThreadPoolExecutor, as_completed |
优势:
- 自动管理线程生命周期,避免频繁创建开销;
- 方便获取任务结果(
future.result()); - 支持批量提交任务,简化代码。
线程 vs 进程:如何选择?
| 场景 | 推荐使用 | 原因分析 |
|---|---|---|
| I/O 密集型任务 | 线程(Thread) | 线程切换成本低,适合等待时间长的任务(如网络请求) |
| CPU 密集型任务 | 进程(Process) | 避开 GIL 限制,利用多核 CPU 并行计算 |
| 共享数据频繁 | 线程 | 线程共享内存空间,数据交互方便 |
| 数据隔离要求高 | 进程 | 进程内存独立,互不干扰,安全性高 |
常见问题与避坑指南
GIL 导致的 CPU 密集型性能问题Python 的 GIL 限制同一时间只有一个线程执行 Python 字节码,因此多线程不适合 CPU 密集型任务(如大规模计算),建议用多进程(
multiprocessing)。死锁风险多个线程互相等待对方释放锁会导致死锁,避免方式:
- 按固定顺序获取锁;
- 使用
with语句自动管理锁(with lock: ...替代acquire()/release())。
1
2
3# 安全的锁使用方式(自动释放)
with lock:
count += 1 # 临界区操作
- 全局变量的线程安全非原子操作(如
count += 1、列表append()后pop())需加锁保护,原子操作(如简单赋值a = 1)无需同步。
实战案例:多线程爬取网页
1 | import threading |
说明:网络请求属于 I/O 密集型任务,多线程能显著减少总耗时(无需等待前一个请求完成再发起下一个)。
总结
Python3 线程是处理并发任务的重要工具,尤其适合 I/O 密集型场景。核心要点:
- 通过
threading.Thread或继承类创建线程,start()启动,join()等待结束; - 用锁(
Lock)解决共享资源竞争问题; - 线程池(
ThreadPoolExecutor)高效管理线程,推荐优先使用; - 避开 GIL 限制,CPU 密集型任务优先选多进程