aiomultiprocess不能直接替换multiprocessing,因为它要求子进程运行独立事件循环,协程必须由asyncio.run()驱动,且需用Pool.map_async而非Process.start;函数须顶层定义、不可引用父进程loop,异常需内部捕获。

aiomultiprocess 为什么不能直接替换 multiprocessing
因为 aiomultiprocess 不是 multiprocessing 的简单包装,它要求每个子进程必须运行独立的事件循环,且所有协程任务必须在 ProcessPoolExecutor 或 Worker 内部被 asyncio.run() 驱动。直接把同步函数或未 await 的协程丢进去会报 RuntimeError: asyncio.run() cannot be called from a running event loop 或 TypeError: object ... can't be used in 'await' expression。
正确启动方式:用 Pool.map_async 而非 Process.start
aiomultiprocess 的核心是 Pool 类,它封装了进程创建、事件循环初始化和协程调度。不要手动调用 multiprocessing.Process,否则无法自动注入事件循环。
- ✅ 正确写法:
async with Pool() as pool: result = await pool.map(func, items) - ❌ 错误写法:
p = multiprocessing.Process(target=func); p.start()—— 这里func若含await会直接崩溃 -
Pool默认使用spawn启动方式(不是fork),避免 Unix 下 fork 后事件循环状态混乱
协程函数必须可序列化,且不能引用闭包中的 event loop
aiomultiprocess 依赖 pickle 在进程间传递函数,所以:
- 协程函数必须定义在模块顶层(不能嵌套在类方法或闭包内),否则
pickle失败,报AttributeError: Can't pickle local object - 不能在函数内显式调用
asyncio.get_event_loop()或依赖当前 loop —— 子进程没有父进程的 loop 实例 - 所有异步依赖(如
aiohttp.ClientSession)必须在协程内部创建,不能作为参数传入 - 示例安全写法:
async def fetch_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url, timeout=10) as resp:
return resp.status性能陷阱:别让单个进程成为瓶颈
aiomultiprocess 的并发能力 = 进程数 × 单进程内协程并发度。但默认 Pool 只开 os.cpu_count() 个进程,而每个进程默认只跑一个协程——这等于退化成同步多进程。
立即学习“Python免费学习笔记(深入)”;
- 务必设置
worker_per_core=2或更高(例如Pool(4, worker_per_core=5)表示 4 核 × 5 协程 = 20 并发协程) - 注意
aiohttp连接池限制:默认ClientSession最大连接数为 100,若单进程发起 200 个请求,会排队阻塞,需显式配置connector=aiohttp.TCPConnector(limit=500) - 内存占用随进程数线性增长,每个进程都带完整 Python 解释器和事件循环,
os.cpu_count() * 2通常是安全上限
真正容易被忽略的是:子进程内协程的异常不会自动冒泡到主进程。如果某个 fetch_url 报 aiohttp.ClientConnectionError,pool.map 返回的结果列表里对应位置是 None 或直接中断,而不是抛出可捕获的异常 —— 必须在协程内部 try/except 并显式返回错误信息。


















