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

如何在python-telegram-bot v20+中异步运行Telegram机器人轮询且不阻塞主应用

如何在python-telegram-bot v20+中异步运行Telegram机器人轮询且不阻塞主应用

哥们,我之前在python-telegram-bot v20+里也踩过这个坑!这个版本完全切换到asyncio异步架构了,和老版本的处理逻辑不一样,之前瞎用线程或者直接asyncio.create_task()踩了一堆循环冲突的错误,后来摸清楚了正确的玩法,给你两个靠谱的方案:

方案一:主应用本身是异步的情况(推荐)

如果你的主应用逻辑也是异步的,直接用asyncio.gather()把机器人轮询和主应用任务放在同一个事件循环里调度就行,这样两者都不会互相阻塞。

示例代码:

import asyncio
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes

# 机器人的命令处理器
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text('Hello!')

# 你的主应用异步逻辑,比如定时任务、其他异步操作
async def main_app_task():
    while True:
        print("主应用在正常运行...")
        await asyncio.sleep(5)

async def main():
    # 初始化机器人应用
    application = ApplicationBuilder().token("你的机器人Token").build()
    application.add_handler(CommandHandler("start", start))

    # 同时启动轮询和主应用任务
    await asyncio.gather(
        application.run_polling(),
        main_app_task()
    )

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

原理很简单:application.run_polling()本身就是个异步协程,asyncio.gather()会把它和你的主应用协程一起扔进事件循环,由asyncio自动调度执行,完全不会出现阻塞的问题。

方案二:主应用是同步的情况

如果你的主应用是同步代码(比如传统的脚本、同步Web框架),那得把机器人的异步逻辑放到单独的线程里,而且要注意在子线程里单独创建事件循环,不能和主线程的循环冲突。

示例代码:

import asyncio
import threading
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes

# 机器人命令处理器
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text('Hello!')

# 子线程中运行机器人的逻辑
def run_bot_in_thread():
    # 必须在子线程里新建一个事件循环,不然会和主线程的循环冲突
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    
    application = ApplicationBuilder().token("你的机器人Token").build()
    application.add_handler(CommandHandler("start", start))
    # 在子线程的循环里运行轮询
    loop.run_until_complete(application.run_polling())

# 你的同步主应用逻辑
def main_sync_app():
    while True:
        print("同步主应用在正常运行...")
        # 模拟主应用的同步工作
        import time
        time.sleep(3)

if __name__ == "__main__":
    # 把机器人启动在后台线程,设置daemon=True让主应用退出时线程自动结束
    bot_thread = threading.Thread(target=run_bot_in_thread, daemon=True)
    bot_thread.start()

    # 运行你的同步主应用
    main_sync_app()

为啥你之前会踩坑?

你遇到的RuntimeError: This event loop is already running或者Cannot close a running event loop,本质都是事件循环的冲突:

  • 如果你在已经运行的事件循环里直接调用asyncio.create_task(application.run_polling()),但又没正确把它加入到当前循环的调度里,或者重复启动循环;
  • 用线程但没在子线程新建事件循环,直接用主线程的循环,导致两个线程抢同一个循环资源。

额外注意点

  • 别多次调用asyncio.run(),它每次都会新建一个事件循环,运行完就关闭,重复调用必然出问题;
  • 如果用的是FastAPI这类异步Web框架,可以把机器人轮询加到框架的后台任务里,逻辑和方案一类似;
  • 要是用Flask、Django这种同步Web框架,直接用方案二的线程方式就行,别在框架的请求上下文里碰机器人的异步逻辑。

备注:内容来源于stack exchange,提问作者Uccio Maselli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 18:14:29