多进程和多线程
🧩 多任务编程基础
✅ 什么是多任务?
多任务是指在同一时间段内执行多个任务。其核心目的是:
- 提高 CPU 使用率
- 提升程序执行效率
- 更好地响应用户交互或外部事件
🔁 并发 vs 并行
| 类型 | 定义 | 适用场景 |
|---|---|---|
| 并发 | 在一段时间内交替执行多个任务(单核 CPU) | 单核处理多个任务 |
| 并行 | 在同一时刻执行多个任务(多核 CPU) | 多核加速计算任务 |
- Python 中由于 GIL(全局解释器锁)的存在,多线程不能真正实现并行计算
- 要实现真正的并行,应使用多进程
🧱 多进程(Multiprocessing)
📌 进程定义
- 进程是操作系统中正在运行的程序实例。
- 是资源分配的基本单位,拥有独立的内存空间。
📌 线程与进程的关系
| 概念 | 特点 |
|---|---|
| 进程 | 分配资源的基本单位,每个进程都有自己的内存空间 |
| 线程 | 同一进程下的线程共享该进程的内存空间 |
🔧 基本使用流程(Python)
import multiprocessing
def worker():
print("子进程执行中...")
if __name__ == '__main__':
p = multiprocessing.Process(target=worker)
p.start()
🧾 获取进程编号
import os
print("当前进程 PID:", os.getpid()) # 当前进程编号
print("父进程 PID:", os.getppid()) # 创建当前进程的父进程编号
🧑🦱 给进程命名 & 获取名字
p = multiprocessing.Process(target=worker, name="WorkerProcess")
print(p.name) # 输出: WorkerProcess
📥 传递参数给子进程
# 使用 args(元组)
p = multiprocessing.Process(target=worker, args=(5,))
# 使用 kwargs(字典)
p = multiprocessing.Process(target=worker, kwargs={"count": 5})
⚠️ 多进程注意事项
- 进程之间不共享全局变量
- 主进程默认等待子进程结束
- 让子进程随主进程结束而终止
p.daemon = True # 设置为守护进程
p.start()
- 手动终止子进程
p.terminate() # 强制结束子进程
🧵 多线程(Threading)
📌 线程定义
- 线程是依附于进程存在的最小执行单元
- 同一个进程中的所有线程共享内存空间
- 是调度执行的基本单位
🔧 基本使用流程(Python)
import threading
def worker():
print("子线程执行中...")
t = threading.Thread(target=worker)
t.start()
⚠️ 多线程注意事项
- 线程无序执行
- 主线程默认等待所有子线程完成
- 设置守护线程(让子线程随主线程一起结束)
t = threading.Thread(target=worker, daemon=True)
# 或者
t.setDaemon(True)
- 线程间共享全局变量
🧷 共享变量问题及解决方法
🧨 问题:数据冲突
当多个线程同时修改同一个变量时,可能导致结果错误。
✅ 解决方案:
方法 1:join()
t.join() # 主线程等待该线程完成再继续
方法 2:互斥锁(Mutex Lock)
import threading
mutex = threading.Lock() # 创建锁
def change_count():
global count
mutex.acquire() # 上锁
try:
count += 1
finally:
mutex.release() # 释放锁
💀 死锁问题(Deadlock)
死锁条件:一个线程反复申请同一把锁,未释放就再次上锁。
mutex.acquire()
mutex.acquire() # ❗死锁!程序卡住
✅ 避免死锁的方法:
- 使用
with语句自动管理锁 - 控制锁的粒度
- 避免嵌套加锁
🧰 线程池与进程池(推荐使用方式)
在实际开发中,频繁创建和销毁线程/进程会带来较大的性能开销。为了提高效率,可以使用线程池和进程池来复用已有的线程或进程。
📌 导入模块
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
🧵 线程池(ThreadPoolExecutor)
适用于 I/O 密集型任务(如网络请求、文件读写等)
from concurrent.futures import ThreadPoolExecutor
import time
def task(n):
time.sleep(1)
return f"Task {n} done"
with ThreadPoolExecutor(max_workers=3) as executor:
results = [executor.submit(task, i) for i in range(5)]
for future in results:
print(future.result())
🧱 进程池(ProcessPoolExecutor)
适用于 CPU 密集型任务(如图像处理、科学计算等)
from concurrent.futures import ProcessPoolExecutor
import time
def cpu_task(n):
sum(i*i for i in range(10000))
return f"CPU Task {n} done"
with ProcessPoolExecutor(max_workers=4) as executor:
results = [executor.submit(cpu_task, i) for i in range(5)]
for future in results:
print(future.result())
🧮 map 方法简化调用
with ThreadPoolExecutor() as executor:
results = executor.map(task, range(5))
for result in results:
print(result)
📌 总结对比表:线程池 vs 进程池
| 对比项 | 线程池(ThreadPoolExecutor) | 进程池(ProcessPoolExecutor) |
|---|---|---|
| 底层机制 | 复用线程 | 复用进程 |
| 适用任务类型 | I/O 密集型 | CPU 密集型 |
| 内存占用 | 小 | 大 |
| 启动速度 | 快 | 相对慢 |
| 是否受 GIL 影响 | 是 | 否 |
| 数据共享 | 可以共享全局变量 | 不共享,需通过 IPC 通信 |
🧠 总结建议
| 场景 | 推荐方式 |
|---|---|
| 处理大量网络请求 | 多线程 / 线程池 |
| 文件读写 | 多线程 / 线程池 |
| 图像处理 / 科学计算 | 多进程 / 进程池 |
| 需要快速启动任务 | 线程池 |
| 需要充分利用多核 CPU | 进程池 |
| 线程安全访问共享资源 | 使用互斥锁 / with lock |

浙公网安备 33010602011771号