在 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 Process
import os
def 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 12345
Task B is running in process 12346
All processes completed
2. 进程池(multiprocessing.Pool
)
当需要管理大量进程时,使用进程池(Pool
)可以更方便地分配和调度任务。
from multiprocessing import Pool
def worker(x):
return x * x
if __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, Queue
def 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: 0
Produced: 1
Produced: 2
Produced: 3
Produced: 4
Consumed: 0
Consumed: 1
Consumed: 2
Consumed: 3
Consumed: 4
2. 使用 Pipe
Pipe
提供了双向通信的能力。
from multiprocessing import Process, Pipe
def 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:
break
if __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: 0
Sent: 1
Sent: 2
Sent: 3
Sent: 4
Received: 0
Received: 1
Received: 2
Received: 3
Received: 4
四、进程同步机制
多进程共享资源时需要同步工具,multiprocessing
提供了 Lock
、Event
等工具。
1. 使用 Lock
Lock
用于防止多个进程同时访问共享资源。
from multiprocessing import Process, Lock
counter = 0
def worker(lock):
global counter
for _ in range(100000):
with lock:
counter += 1
if __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, Event
def 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 |
编程难度 | 相对复杂 | 较简单 | 语法直观,使用方便 |
六、总结与推荐
-
选择多进程:
- 当任务是 CPU 密集型,需要并行处理时,优先考虑
multiprocessing
。 - 适合需要进程隔离的场景,避免共享资源引发的数据竞争。
- 当任务是 CPU 密集型,需要并行处理时,优先考虑
-
选择多线程:
- 适用于 I/O 密集型任务,例如文件操作、网络请求。
- 如果任务需要共享内存并发处理,多线程更方便。
-
选择协程:
- 在高并发的 I/O 密集型任务 中(如异步网络请求),协程是最优选择。
- 轻量、性能高,适合现代异步编程。
通过合理选择工具,可以在 Python 中充分利用多进程、多线程和协程的优势,打造高性能的并发程序。