异步pyTelegramBotApi跨线程调用send_message请求超时问题
问题诊断与解决
核心问题
错误根源在于跨线程调用异步方法时的事件循环冲突:
- Telegram机器人的异步客户端
tg_bot运行在主事件循环中(由main函数启动并执行infinity_polling) - VK的监听循环是同步阻塞的,运行在单独线程中,你用
asyncio.run(tg_bot.send_post(...))会创建一个全新的独立事件循环,这个新循环无法复用主循环中已建立的Telegram连接会话,导致请求超时。
修复方案
1. 保存主事件循环引用
修改启动代码,把主事件循环保存下来,供其他线程调用:
import asyncio import threading # 全局保存主事件循环引用 main_loop = None async def main(): global main_loop main_loop = asyncio.get_running_loop() vk_thread = threading.Thread(target=run_vk_loop) vk_thread.start() await tg_bot.init() if __name__ == "__main__": asyncio.run(main())
2. 修改VK循环中的异步调用方式
在VK的监听循环里,不要用asyncio.run,而是用asyncio.run_coroutine_threadsafe把异步任务提交到主事件循环:
import asyncio import time def init(): print("Starting VK Cycle") try: for event in longpoll.listen(): print("There's a VK event") event_received = False while not event_received: print("Trying to receive it...") try: if event.type == VkBotEventType.WALL_POST_NEW: print("It's a new post on the wall") post = event.object if post.post_type == 'suggest': print("The new post is a suggested post") # 替换asyncio.run,用线程安全的方式提交任务到主循环 future = asyncio.run_coroutine_threadsafe( tg_bot.send_post(text=post.text, post_id=post.id), main_loop ) # 可选:获取执行结果,避免任务异常无感知 try: future.result(timeout=10) except Exception as e: print(f"Send post failed: {e}") event_received = True except requests.exceptions.ReadTimeout as e: print("ERROR: ReadTimeout. Waiting for 3 seconds and trying to reconnect") # 用with语句更安全地操作日志文件 with open("cogparty.log", "a") as log_file: log_file.write( f"\n\nvkontakte.py init() INNER try-catch error:\n\n{str(e)}\n\nTrying again in 3 seconds...") time.sleep(3) except Exception as e: print(f"ERROR: {type(e).__name__}::{str(e)}") with open("cogparty.log", "a") as log_file: log_file.write(f"\n\nvkontakte.py init() OUTER try-catch error:\n\n{str(e)}")
3. 额外优化建议
- 日志操作统一用
with语句,避免文件句柄泄漏 - 给
future.result()设置合理超时,防止VK线程被长时间阻塞 - 确保
my_chat_id、gen_markup()等变量/方法的线程安全性(如果存在状态共享)
为什么之前能运行?
之前偶然正常是因为新创建的事件循环侥幸建立了新的Telegram连接,但这种方式本身不稳定,长时间运行后会因为资源耗尽或连接冲突导致崩溃,这也是现在启动就失败的原因。
内容的提问来源于stack exchange,提问作者MeloMan
相关产品推荐
相关产品推荐

