多进程和多线程

🧩 多任务编程基础

✅ 什么是多任务?

多任务是指在同一时间段内执行多个任务。其核心目的是:

  • 提高 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})

⚠️ 多进程注意事项

  1. 进程之间不共享全局变量
  2. 主进程默认等待子进程结束
  3. 让子进程随主进程结束而终止
p.daemon = True     # 设置为守护进程
p.start()
  1. 手动终止子进程
p.terminate()       # 强制结束子进程

🧵 多线程(Threading)

📌 线程定义

  • 线程是依附于进程存在的最小执行单元
  • 同一个进程中的所有线程共享内存空间
  • 调度执行的基本单位

🔧 基本使用流程(Python)

import threading

def worker():
    print("子线程执行中...")

t = threading.Thread(target=worker)
t.start()

⚠️ 多线程注意事项

  1. 线程无序执行
  2. 主线程默认等待所有子线程完成
  3. 设置守护线程(让子线程随主线程一起结束)
t = threading.Thread(target=worker, daemon=True)

# 或者
t.setDaemon(True)
  1. 线程间共享全局变量

🧷 共享变量问题及解决方法

🧨 问题:数据冲突

当多个线程同时修改同一个变量时,可能导致结果错误。

✅ 解决方案:

方法 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

posted @ 2025-06-04 11:22  玉米面手雷王  阅读(15)  评论(0)    收藏  举报