Python多进程
Python 提供了 multiprocessing 模块,用于多进程编程。与多线程不同,多进程通过创建独立的内存空间来运行任务,从而绕过了 GIL(全局解释器锁)的限制,非常适合 CPU 密集型任务。
以下是 Python 多进程编程的基本用法和示例:
1. 基本使用
multiprocessing 模块中的 Process 类用于创建子进程。
示例:创建子进程
from multiprocessing import Process
import os
import time
def worker(name):
print(f"Process {name} (PID: {os.getpid()}) is running...")
time.sleep(2)
print(f"Process {name} is done.")
if __name__ == "__main__":
print(f"Main process PID: {os.getpid()}")
# 创建并启动子进程
p1 = Process(target=worker, args=("A",))
p2 = Process(target=worker, args=("B",))
p1.start()
p2.start()
# 等待子进程结束
p1.join()
p2.join()
print("All processes are done.")
2. 使用 Pool 创建进程池
multiprocessing.Pool 是一个方便的接口,用于并发执行多个任务。
示例:进程池的 map
from multiprocessing import Pool
import time
def task(n):
time.sleep(1)
return n * n
if __name__ == "__main__":
with Pool(4) as pool: # 创建包含 4 个进程的进程池
results = pool.map(task, range(5)) # 提交任务
print(results)
示例:进程池的 apply_async
apply_async 提供了异步任务提交方式,并允许通过回调函数处理结果。
from multiprocessing import Pool
import time
def task(n):
time.sleep(1)
return f"Task {n} done"
def callback(result):
print(result)
if __name__ == "__main__":
with Pool(4) as pool:
for i in range(5):
pool.apply_async(task, args=(i,), callback=callback) # 提交异步任务
pool.close() # 禁止提交新任务
pool.join() # 等待所有任务完成
3. 数据共享
多进程之间的数据是隔离的,但可以通过 Value 和 Array 来共享数据,或者使用 Manager 来管理共享资源。
示例:共享变量
from multiprocessing import Process, Value
def increment(shared_num):
for _ in range(1000):
with shared_num.get_lock(): # 加锁,确保线程安全
shared_num.value += 1
if __name__ == "__main__":
shared_num = Value('i', 0) # 共享整数变量,初始值为 0
processes = [Process(target=increment, args=(shared_num,)) for _ in range(4)]
for p in processes:
p.start()
for p in processes:
p.join()
print(f"Final value: {shared_num.value}")
4. 使用 Manager 管理共享数据
multiprocessing.Manager 提供了字典、列表等可共享的数据结构。
示例:共享列表
from multiprocessing import Process, Manager
def worker(shared_list):
for i in range(5):
shared_list.append(i)
if __name__ == "__main__":
with Manager() as manager:
shared_list = manager.list() # 创建共享列表
processes = [Process(target=worker, args=(shared_list,)) for _ in range(4)]
for p in processes:
p.start()
for p in processes:
p.join()
print(f"Shared list: {shared_list}")
5. 捕获异常
可以通过 try 和 except 捕获子进程中的异常。
示例:捕获异常
from multiprocessing import Process
def worker(n):
if n == 2:
raise ValueError("Something went wrong!")
print(f"Process {n} done.")
if __name__ == "__main__":
processes = [Process(target=worker, args=(i,)) for i in range(5)]
for p in processes:
p.start()
for p in processes:
p.join()
6. 注意事项
- 使用
if __name__ == "__main__"在 Windows 系统上,必须将多进程代码放在if __name__ == "__main__"中,否则会引发无限递归。 - 避免全局变量的使用 各个进程拥有独立的内存空间,修改全局变量不会影响其他进程。
- 多进程适合 CPU 密集型任务 对于 I/O 密集型任务,可以优先考虑多线程。