You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确将同步函数转为异步函数?Telegram Bot遇序列化问题

问题解决:ProcessPoolExecutor 序列化函数报错的修复方案

错误原因

你遇到的PicklingError是因为:装饰器将原同步函数sync_func替换成了包装后的异步函数,而包装函数内部引用的原未装饰的sync_func,在子进程的__main__模块中不存在(子进程启动时导入__main__,此时sync_func是被装饰后的异步函数)。pickle序列化时会校验函数的身份一致性,发现主进程和子进程中的sync_func不是同一个对象,因此报错。


修复方案

方案1:调整装饰器实现+分离同步函数到单独模块

把耗时的同步逻辑放到单独的模块中(避免主进程和子进程的函数身份冲突),同时修改装饰器,不使用partial打包函数,直接传递函数和参数到进程池:

  1. 创建tasks.py文件,存放同步逻辑:
# tasks.py
def sync_process_video(a):
    # 这里替换成你的视频下载/压缩逻辑
    import time
    time.sleep(10)
    return f"处理完成:{a}"
  1. 主文件代码:
import asyncio
from concurrent.futures import ProcessPoolExecutor
from functools import wraps
from tasks import sync_process_video

executor = ProcessPoolExecutor()

def async_wrap(func):
    @wraps(func)
    async def run(*args, **kwargs):
        loop = asyncio.get_running_loop()
        # 直接传递函数和参数,避免partial打包导致的序列化问题
        return await loop.run_in_executor(executor, func, *args, **kwargs)
    
    return run 

# 装饰同步函数
async_process_video = async_wrap(sync_process_video)

async def main():
    result = await async_process_video(4)
    print(result)

if __name__ == "__main__":
    asyncio.run(main())

方案2:去掉装饰器,直接调用进程池(更简洁)

不需要装饰器,直接在异步函数中通过loop.run_in_executor提交同步任务,这是更简单的实现方式:

import asyncio
from concurrent.futures import ProcessPoolExecutor
from tasks import sync_process_video

executor = ProcessPoolExecutor()

async def main():
    loop = asyncio.get_running_loop()
    # 直接提交同步任务到进程池,不阻塞事件循环
    result = await loop.run_in_executor(executor, sync_process_video, 4)
    print(result)

if __name__ == "__main__":
    asyncio.run(main())

适配Telegram Bot的建议

在你的Telegram Bot开发中,推荐使用这种方式:

  • 把视频下载、压缩等耗时逻辑封装成独立的同步函数,放在单独模块中
  • 在Bot的异步消息处理函数中,用loop.run_in_executor将耗时任务提交到进程池
  • 这样Bot的事件循环不会被阻塞,能同时响应新的用户请求

内容的提问来源于stack exchange,提问作者Mikhail

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:30:54