Python多进程和多线程的区别:从原理到实战

Python多进程(multiprocessing)和多线程(threading)是实现并发编程的两大核心方式,但适用场景、底层原理、性能表现差异极大。很多新手容易混淆二者,本文从“原理-特性-场景-实战”四层拆解核心区别,结合真实案例说明选型逻辑,帮你彻底搞懂该用哪一种。

一、核心原理:先搞懂“底层逻辑差异”

1. 多线程(threading):进程内的“轻量执行单元”

  • 本质:线程是进程的子集,一个进程可以包含多个线程,所有线程共享进程的内存空间(如全局变量、文件句柄)。
  • GIL限制:Python有“全局解释器锁(GIL)”,同一进程内的多个线程,同一时间只能有一个线程执行Python字节码——这意味着多线程无法利用多核CPU并行执行,只能“交替执行”(并发)。
  • 切换开销:线程切换仅需保存/恢复寄存器状态,开销极低(微秒级)。

2. 多进程(multiprocessing):独立的“程序实例”

  • 本质:每个进程都是独立的Python解释器实例,有自己的内存空间、GIL锁,互不干扰。
  • 多核利用:多个进程可同时运行在不同CPU核心上,真正实现“并行执行”,突破GIL限制。
  • 切换开销:进程切换需要操作系统分配内存、CPU资源,开销高(毫秒级),远大于线程。

二、核心区别对比(表格版,新手速查)

对比维度 多线程(threading) 多进程(multiprocessing)
GIL影响 受GIL限制,无法多核并行 不受GIL限制,可多核并行
内存空间 共享进程内存(易数据共享) 独立内存空间(隔离性强)
切换开销 极低(轻量) 较高(重量级)
启动速度 快(毫秒级) 慢(秒级,需初始化解释器)
通信难度 简单(直接共享变量,需加锁) 复杂(需Queue/Pipe/Manager)
资源占用 低(共享内存,无需重复加载) 高(每个进程独立占用内存)
崩溃影响 一个线程崩溃导致整个进程挂掉 单个进程崩溃不影响其他进程
系统兼容性 跨平台(Windows/Linux/Mac) Windows需加if __name__ == "__main__"

三、性能差异:IO密集型vs CPU密集型

1. 多线程:适合IO密集型任务(优势拉满)

IO密集型任务的核心特征是“等待时间远大于计算时间”(如网络请求、文件读写、数据库操作),此时GIL不会影响性能——线程在等待IO时会释放GIL,其他线程可执行。

实战案例:多线程爬取网页(IO密集)

import threading
import time
import requests

 模拟爬取网页(IO任务)
def crawl(url):
    print(f"线程{threading.current_thread().name}:开始爬取{url}")
    response = requests.get(url)   网络等待(释放GIL)
    print(f"线程{threading.current_thread().name}:{url}爬取完成,状态码{response.status_code}")

if __name__ == "__main__":
    start = time.time()
    urls = ["https://baidu.com", "https://github.com", "https://python.org"]
    
     多线程执行
    threads = []
    for i, url in enumerate(urls):
        t = threading.Thread(target=crawl, args=(url,), name=f"t{i+1}")
        threads.append(t)
        t.start()
    
    for t in threads:
        t.join()
    
    print(f"总耗时:{time.time() - start:.2f}秒")   约2秒(并发执行)

单线程对比:单线程执行需约6秒,多线程效率提升3倍。

2. 多进程:适合CPU密集型任务(唯一选择)

CPU密集型任务的核心特征是“计算时间占主导”(如数据运算、矩阵乘法、图片处理),此时GIL会让多线程失效,而多进程可利用多核CPU并行计算。

实战案例:多进程计算平方(CPU密集)

import multiprocessing
import time
import numpy as np

 模拟矩阵运算(CPU任务)
def calculate(matrix):
    print(f"进程{multiprocessing.current_process().pid}:开始计算")
    result = np.dot(matrix, matrix)   纯计算(占用CPU)
    return result.shape

if __name__ == "__main__":
    start = time.time()
     生成3个大矩阵(每个1000x1000)
    matrices = [np.random.rand(1000, 1000) for _ in range(3)]
    
     多进程执行
    processes = []
    results = []
    for mat in matrices:
        p = multiprocessing.Process(target=lambda m: results.append(calculate(m)), args=(mat,))
        processes.append(p)
        p.start()
    
    for p in processes:
        p.join()
    
    print(f"总耗时:{time.time() - start:.2f}秒")   约3秒(并行执行)

多线程对比:多线程执行需约9秒,多进程效率提升3倍(4核CPU)。

四、关键细节:通信与同步

1. 多线程:共享数据易,但需处理线程安全

线程共享进程内存,直接修改全局变量即可通信,但多个线程同时修改会导致“数据竞争”,需用Lock/RLock加锁:

import threading

count = 0
lock = threading.Lock()

def add():
    global count
    for _ in range(100000):
        with lock:   加锁保证原子操作
            count += 1

if __name__ == "__main__":
    t1 = threading.Thread(target=add)
    t2 = threading.Thread(target=add)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(count)   加锁后输出200000,不加锁可能小于200000

2. 多进程:数据隔离,需用专用工具通信

进程内存隔离,无法直接共享变量,需通过Queue(队列)、Pipe(管道)、Manager(共享数据结构)通信:

import multiprocessing

 用Queue传递数据
def producer(queue):
    queue.put("Hello 多进程")

def consumer(queue):
    print(queue.get())   输出:Hello 多进程

if __name__ == "__main__":
    q = multiprocessing.Queue()
    p1 = multiprocessing.Process(target=producer, args=(q,))
    p2 = multiprocessing.Process(target=consumer, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

五、选型指南:什么时候用线程?什么时候用进程?

优先用多线程的场景

  1. IO密集型任务:爬虫、接口调用、文件读写、数据库查询;
  2. 需要频繁数据共享:任务间需频繁交换数据(如实时统计);
  3. 轻量并发:任务数量多、启动频繁(如定时检测小任务)。

优先用多进程的场景

  1. CPU密集型任务:数据计算、矩阵运算、图片/视频处理、密码破解;
  2. 需要稳定性:单个任务崩溃不影响整体(如批量处理独立文件);
  3. 充分利用多核:服务器有多个CPU核心,需最大化算力。
posted @ 2025-12-08 14:27  布衣开发者  阅读(0)  评论(0)    收藏  举报