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()
问题原因
- 事件循环不兼容:Telegram Bot的
Update对象绑定在application.run_polling()启动的主事件循环上,但你用独立线程+asyncio.run()创建了新的事件循环,跨循环调用update.message.reply_text()会触发报错——因为对象属于另一个事件循环。 - 手动队列+线程的异步处理错误:手动维护列表队列,线程里用
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
相关产品推荐
相关产品推荐

