> 技术文档 > 深入理解 Python 的多进程编程 (Multiprocessing)_python multiprocessing

深入理解 Python 的多进程编程 (Multiprocessing)_python multiprocessing

在 Python 中,multiprocessing 模块提供了多进程支持,是处理并发任务的一个核心工具。与多线程不同,多进程可以突破 GIL(Global Interpreter Lock,全局解释器锁)的限制,充分利用多核 CPU 进行并行计算。本文将详细介绍 Python 中的多进程编程,包括其基础用法、进程间通信、同步机制,以及与线程和协程的对比。


一、为什么选择多进程?

1. GIL 的限制

Python 的 GIL 限制了多线程的并行能力,同一时间只能有一个线程执行 Python 字节码。对于 CPU 密集型任务,多线程不能充分利用多核 CPU。

多进程通过创建独立的进程,每个进程拥有独立的 GIL,可以并行执行任务,适合需要大量计算的场景。

2. 适用场景

  • CPU 密集型任务:如科学计算、视频处理、大量数据的复杂运算。
  • 任务隔离需求:每个进程有独立的内存空间,减少了竞争资源的风险。

二、multiprocessing 的基础用法

1. 创建子进程

使用 multiprocessing.Process 类可以轻松创建子进程。

from multiprocessing import Processimport osdef worker(task_name): print(f\"Task {task_name} is running in process {os.getpid()}\")if __name__ == \"__main__\": process1 = Process(target=worker, args=(\"A\",)) process2 = Process(target=worker, args=(\"B\",)) process1.start() process2.start() process1.join() process2.join() print(\"All processes completed\")

输出

Task A is running in process 12345Task B is running in process 12346All processes completed

2. 进程池(multiprocessing.Pool

当需要管理大量进程时,使用进程池(Pool)可以更方便地分配和调度任务。

from multiprocessing import Pooldef worker(x): return x * xif __name__ == \"__main__\": with Pool(4) as pool: # 创建包含 4 个进程的进程池 results = pool.map(worker, range(10)) print(results)

输出

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

三、进程间通信

Python 提供了多种方式实现进程间通信,包括队列(Queue)、管道(Pipe)和事件(Event)。

1. 使用 Queue

Queue 提供了线程安全的队列,可以实现进程间的数据共享。

from multiprocessing import Process, Queuedef producer(queue): for i in range(5): queue.put(i) print(f\"Produced: {i}\")def consumer(queue): while not queue.empty(): item = queue.get() print(f\"Consumed: {item}\")if __name__ == \"__main__\": q = Queue() p1 = Process(target=producer, args=(q,)) p2 = Process(target=consumer, args=(q,)) p1.start() p1.join() p2.start() p2.join()

输出

Produced: 0Produced: 1Produced: 2Produced: 3Produced: 4Consumed: 0Consumed: 1Consumed: 2Consumed: 3Consumed: 4

2. 使用 Pipe

Pipe 提供了双向通信的能力。

from multiprocessing import Process, Pipedef sender(pipe): for i in range(5): pipe.send(i) print(f\"Sent: {i}\") pipe.close()def receiver(pipe): while True: try: item = pipe.recv() print(f\"Received: {item}\") except EOFError: breakif __name__ == \"__main__\": parent_conn, child_conn = Pipe() p1 = Process(target=sender, args=(parent_conn,)) p2 = Process(target=receiver, args=(child_conn,)) p1.start() p2.start() p1.join() p2.join()

输出

Sent: 0Sent: 1Sent: 2Sent: 3Sent: 4Received: 0Received: 1Received: 2Received: 3Received: 4

四、进程同步机制

多进程共享资源时需要同步工具,multiprocessing 提供了 LockEvent 等工具。

1. 使用 Lock

Lock 用于防止多个进程同时访问共享资源。

from multiprocessing import Process, Lockcounter = 0def worker(lock): global counter for _ in range(100000): with lock: counter += 1if __name__ == \"__main__\": lock = Lock() p1 = Process(target=worker, args=(lock,)) p2 = Process(target=worker, args=(lock,)) p1.start() p2.start() p1.join() p2.join() print(f\"Final counter value: {counter}\")

2. 使用 Event

Event 是一种简单的线程同步原语,用于让一个进程等待另一个进程发出信号。

from multiprocessing import Process, Eventdef worker(event): print(\"Worker waiting for event to be set...\") event.wait() # 等待事件被设置 print(\"Worker received event signal, starting work!\")if __name__ == \"__main__\": event = Event() p = Process(target=worker, args=(event,)) p.start() print(\"Main process performing some setup...\") import time time.sleep(2) print(\"Main process setting event.\") event.set() # 触发事件 p.join()

输出

Worker waiting for event to be set...Main process performing some setup...Main process setting event.Worker received event signal, starting work!

五、多进程、线程与协程的对比

特性 多进程(multiprocessing) 多线程(threading) 协程(asyncio适用场景 CPU 密集型任务 I/O 密集型任务,简单并发 I/O 密集型任务,高性能并发 多核支持 可充分利用多核 受限于 GIL,无多核支持 单线程实现并发,无多核支持 资源隔离 每个进程独立,资源隔离性高 线程共享内存,隔离性较低 协程运行在同一线程,隔离性低 资源开销 进程上下文切换开销高 线程上下文切换开销较低 协程更轻量,占用资源更少 通信方式 队列(Queue)、管道(Pipe) 共享内存或队列 事件循环或 asyncio.Queue 编程难度 相对复杂 较简单 语法直观,使用方便

六、总结与推荐

  1. 选择多进程

    • 当任务是 CPU 密集型,需要并行处理时,优先考虑 multiprocessing
    • 适合需要进程隔离的场景,避免共享资源引发的数据竞争。
  2. 选择多线程

    • 适用于 I/O 密集型任务,例如文件操作、网络请求。
    • 如果任务需要共享内存并发处理,多线程更方便。
  3. 选择协程

    • 在高并发的 I/O 密集型任务 中(如异步网络请求),协程是最优选择。
    • 轻量、性能高,适合现代异步编程。

通过合理选择工具,可以在 Python 中充分利用多进程、多线程和协程的优势,打造高性能的并发程序。