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

如何在后台运行asyncio协程?——基于aiogram调度器的实践疑问

解决aiogram协程后台运行不阻塞主线程的问题

我明白你从JS转过来的困惑——JS的事件循环是环境自动管理的,而Python的asyncio需要我们手动掌控事件循环的运行逻辑,这也是你之前各种尝试碰壁的原因。咱们一步步拆解问题,找到符合你需求的方案:

核心误区:Python asyncio的单线程事件循环

首先得明确:Python的asyncio是单线程事件循环模型,所有协程都在同一个线程的事件循环里调度执行。你不能期望像JS那样直接调用协程就让它在“后台”跑——必须把协程交给事件循环管理,同时让事件循环持续运行,还要把你的其他任务也纳入这个循环的调度中。

正确的实现方式

如果你不想用线程,那所有需要并发执行的逻辑都得写成协程,然后让它们在同一个事件循环里被调度。这里有两种简洁的实现方式:

方式1:用asyncio.gather()同时启动多个协程(推荐Python3.7+)

把start_polling和你的自定义任务都放进asyncio.gather(),再用asyncio.run()启动事件循环,这样两个协程会被并发执行:

import asyncio
from aiogram import Bot, Dispatcher
from aiogram.types import Message

# 初始化bot和调度器
API_TOKEN = "你的Telegram Bot Token"
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)

# 你的自定义后台任务(必须是协程函数)
async def my_custom_task():
    while True:
        print("正在执行主线程的其他任务...")
        await asyncio.sleep(3)  # 关键:必须有await让事件循环切换任务

# aiogram的消息处理器示例
@dp.message_handler(commands=["start"])
async def send_welcome(message: Message):
    await message.reply("Hello! 我在运行的同时,后台任务也没停~")

async def main():
    # 同时启动polling和自定义任务
    await asyncio.gather(
        dp.start_polling(),
        my_custom_task()
    )

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

方式2:用create_task提交任务+事件循环run_forever

如果你需要更灵活地控制事件循环(比如手动停止),可以用这种方式:

import asyncio
from aiogram import Bot, Dispatcher

API_TOKEN = "你的Telegram Bot Token"
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)

async def my_custom_task():
    while True:
        print("自定义任务在运行...")
        await asyncio.sleep(3)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    # 把两个任务都提交到事件循环
    loop.create_task(dp.start_polling())
    loop.create_task(my_custom_task())
    # 启动事件循环(会阻塞直到调用loop.stop())
    loop.run_forever()

为什么你之前的尝试失败?

  • 直接调用dp.start_polling():Python会提示“协程从未被等待”,因为协程必须被await或者交给事件循环管理,不能直接执行。
  • run_until_complete(dp.start_polling()):这个方法会阻塞线程,直到传入的协程执行完毕——但start_polling是无限循环的,所以线程会一直被占用。
  • create_task+run_forever:如果你只提交了start_polling一个任务,那事件循环只会跑这个任务;但如果同时提交多个任务,它们会被并发调度,只是run_forever本身会阻塞线程(这是正常的,因为事件循环需要持续运行才能调度任务)。

关键注意点

  • 所有需要并发的逻辑必须写成协程函数,并且里面要有足够的await语句——这是事件循环切换任务的触发点,否则某个协程会一直占用事件循环,导致其他任务无法执行。
  • 如果你需要在同步代码里触发协程操作,那得用asyncio.run()或者获取当前事件循环来提交任务,但本质还是要在事件循环的上下文里。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 00:52:27