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

Telegram机器人请求队列报错:事件循环绑定不一致问题

问题描述
  • 开发了一个Telegram机器人,功能是接收小于10的数字并从该数字每秒递减计数到0
  • 需实现请求队列:机器人正在计数时,告知用户已加入队列并按顺序处理任务
  • 处理第二或第三个请求时出现错误,错误信息:

Unknown error in HTTP implementation: Runtime Error('<asyncio.locks.Event object at 0x000002495210ECE0 [unset]> is bound to a different event loop')

  • 尝试用asyncio.new_event_loop()和asyncio.set_event_loop(loop)解决,但机器人无法启动或队列处理失效
原代码
import time, asyncio
from threading import Thread
from telegram import ForceReply, Update
from telegram.constants import ParseMode
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters

TOKEN = ""

queue = []

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_html("Send me a number and I'll count from it to zero")
 
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    msg = update.message.text
    if msg.isdigit():
        msg = int(msg)
        if msg > 0:
            data = {'update': update, 'number': msg}
            queue.append(data)
            pos = len(queue)
            await update.message.reply_text(f"Please wait. you're {pos} in queue")
 
async def count_to_zero(num, update):
    for i in range(num, -1, -1):
            await update.message.reply_text(i)
            time.sleep(2)

def queue_handler() -> None:
    while True:
        if len(queue) > 0:
            update = queue[0]['update']
            num = queue[0]['number']
            asyncio.run(count_to_zero(num, update))
            del queue[0]

thread = Thread(target=queue_handler)
thread.start()

def main() -> None:
    # Create the Application and pass it your bot's token.
    application = Application.builder().token(TOKEN).build()
 
    # on different commands - answer in Telegram
    application.add_handler(CommandHandler("start", start))
 
    # on non command i.e message - echo the message on Telegram
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
 
    # Run the bot until the user presses Ctrl-C
    application.run_polling()
 
if __name__ == "__main__":
    main()
问题原因
  1. 事件循环不兼容:Telegram Bot的Update对象绑定在application.run_polling()启动的主事件循环上,但你用独立线程+asyncio.run()创建了新的事件循环,跨循环调用update.message.reply_text()会触发报错——因为对象属于另一个事件循环。
  2. 手动队列+线程的异步处理错误:手动维护列表队列,线程里用time.sleep()会阻塞线程,且异步函数在不同循环执行导致上下文不匹配。
解决方案

改用asyncio.Queue实现异步队列,在Bot的主事件循环中启动后台任务处理队列,全程用异步操作避免跨循环问题:

import asyncio
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters

TOKEN = ""

# 使用asyncio异步队列替代手动列表
request_queue = asyncio.Queue()

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_html("Send me a number and I'll count from it to zero")

async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    msg = update.message.text
    if msg.isdigit():
        msg = int(msg)
        if 0 < msg < 10:  # 限制小于10的数字,符合需求
            await request_queue.put((update, msg))
            pos = request_queue.qsize()
            await update.message.reply_text(f"Please wait. you're {pos} in queue")

async def count_to_zero(num, update):
    for i in range(num, -1, -1):
        await update.message.reply_text(i)
        await asyncio.sleep(2)  # 用异步sleep,不阻塞事件循环

async def queue_handler():
    while True:
        # 队列为空时自动等待,无需轮询
        update, num = await request_queue.get()
        try:
            await count_to_zero(num, update)
        finally:
            # 标记任务完成,用于队列状态追踪(可选)
            request_queue.task_done()

def main() -> None:
    application = Application.builder().token(TOKEN).build()

    # 添加处理器
    application.add_handler(CommandHandler("start", start))
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))

    # 将队列处理器加入Bot主事件循环,避免跨循环问题
    application.create_task(queue_handler())

    application.run_polling()

if __name__ == "__main__":
    main()
关键修改点
  • 替换手动列表为asyncio.Queue:异步队列原生支持事件循环内的等待、添加操作,无需额外线程。
  • 用await asyncio.sleep(2)替代time.sleep(2):异步sleep不会阻塞事件循环,保证Bot能同时接收新的队列请求。
  • 在主事件循环启动队列任务:通过application.create_task()将队列处理器加入Bot的主事件循环,所有异步操作在同一个循环中执行,彻底解决跨循环绑定问题。
  • 补充数字小于10的限制:匹配最初的功能需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:42:36