Telegram Bot异步线程报错RuntimeError: Event loop is closed求助
问题描述
我需要实现每5秒执行计算任务,并通过Telegram Bot发送计算结果。于是用线程运行主计算函数,主线程运行Bot的run_polling,但代码触发了RuntimeError: Event loop is closed,进而引发telegram.error.NetworkError。
原始代码
import threading from telegram.ext import ApplicationBuilder import asyncio import time botApp = ApplicationBuilder().token(token="******:******").build() def main(): # computation stuff while True: asyncio.run(send_msg()) # send result of computation time.sleep(5) async def send_msg(): print("sending") await botApp.bot.sendMessage( chat_id="********", text='example', parse_mode= 'markdown' ) if __name__ == '__main__': x = threading.Thread(target=main()).start botApp.run_polling()
报错栈
Traceback (most recent call last): File "/Library/Python/3.8/site-packages/anyio/streams/tls.py", line 130, in _call_sslobject_method result = func(*args) File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/ssl.py", line 888, in read v = self._sslobj.read(len) ssl.SSLWantReadError: The operation did not complete (read) (_ssl.c:2607) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/Library/Python/3.8/site-packages/telegram/request/_baserequest.py", line 274, in _request_wrapper code, payload = await self.do_request( File "/Library/Python/3.8/site-packages/telegram/request/_httpxrequest.py", line 199, in do_request res = await self._client.request( File "/Library/Python/3.8/site-packages/httpx/_client.py", line 1533, in request return await self.send(request, auth=auth, follow_redirects=follow_redirects) File "/Library/Python/3.8/site-packages/httpx/_client.py", line 1620, in send response = await self._send_handling_auth( File "/Library/Python/3.8/site-packages/httpx/_client.py", line 1648, in _send_handling_auth response = await self._send_handling_redirects( File "/Library/Python/3.8/site-packages/httpx/_client.py", line 1685, in _send_handling_redirects response = await self._send_single_request(request) File "/Library/Python/3.8/site-packages/httpx/_client.py", line 1722, in _send_single_request response = await transport.handle_async_request(request) File "/Library/Python/3.8/site-packages/httpx/_transports/default.py", line 353, in handle_async_request resp = await self._pool.handle_async_request(req) File "/Library/Python/3.8/site-packages/httpcore/_async/connection_pool.py", line 253, in handle_async_request raise exc File "/Library/Python/3.8/site-packages/httpcore/_async/connection_pool.py", line 237, in handle_async_request response = await connection.handle_async_request(request) File "/Library/Python/3.8/site-packages/httpcore/_async/connection.py", line 90, in handle_async_request return await self._connection.handle_async_request(request) File "/Library/Python/3.8/site-packages/httpcore/_async/http2.py", line 144, in handle_async_request raise exc File "/Library/Python/3.8/site-packages/httpcore/_async/http2.py", line 112, in handle_async_request status, headers = await self._receive_response( File "/Library/Python/3.8/site-packages/httpcore/_async/http2.py", line 229, in _receive_response event = await self._receive_stream_event(request, stream_id) File "/Library/Python/3.8/site-packages/httpcore/_async/http2.py", line 260, in _receive_stream_event await self._receive_events(request, stream_id) File "/Library/Python/3.8/site-packages/httpcore/_async/http2.py", line 281, in _receive_events events = await self._read_incoming_data(request) File "/Library/Python/3.8/site-packages/httpcore/_async/http2.py", line 341, in _read_incoming_data raise exc File "/Library/Python/3.8/site-packages/httpcore/_async/http2.py", line 327, in _read_incoming_data data = await self._network_stream.read(self.READ_NUM_BYTES, timeout) File "/Library/Python/3.8/site-packages/httpcore/backends/asyncio.py", line 34, in read return await self._stream.receive(max_bytes=max_bytes) File "/Library/Python/3.8/site-packages/anyio/streams/tls.py", line 195, in receive data = await self._call_sslobject_method(self._ssl_object.read, max_bytes) File "/Library/Python/3.8/site-packages/anyio/streams/tls.py", line 137, in _call_sslobject_method data = await self.transport_stream.receive() File "/Library/Python/3.8/site-packages/anyio/_backends/_asyncio.py", line 1264, in receive self._transport.resume_reading() File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/selector_events.py", line 798, in resume_reading self._add_reader(self._sock_fd, self._read_ready) File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/selector_events.py", line 744, in _add_reader self._loop._add_reader(fd, callback, *args) File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/selector_events.py", line 256, in _add_reader self._check_closed() File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 508, in _check_closed raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed The above exception was the direct cause of the following exception: Traceback (most recent call last): File "./test.py", line 26, in <module> threading.Thread(target=main()) File "./test.py", line 12, in main asyncio.run(send_msg()) File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/runners.py", line 43, in run return loop.run_until_complete(main) File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete return future.result() File "./test.py", line 18, in send_msg await botApp.bot.sendMessage( File "/Library/Python/3.8/site-packages/telegram/ext/_extbot.py", line 2598, in send_message return await super().send_message( File "/Library/Python/3.8/site-packages/telegram/_bot.py", line 331, in decorator result = await func(*args, **kwargs) # skipcq: PYL-E1102 File "/Library/Python/3.8/site-packages/telegram/_bot.py", line 760, in send_message return await self._send_message( File "/Library/Python/3.8/site-packages/telegram/ext/_extbot.py", line 488, in _send_message result = await super()._send_message( File "/Library/Python/3.8/site-packages/telegram/_bot.py", line 512, in _send_message result = await self._post( File "/Library/Python/3.8/site-packages/telegram/_bot.py", line 419, in _post return await self._do_post( File "/Library/Python/3.8/site-packages/telegram/ext/_extbot.py", line 306, in _do_post return await super()._do_post( File "/Library/Python/3.8/site-packages/telegram/_bot.py", line 450, in _do_post return await request.post( File "/Library/Python/3.8/site-packages/telegram/request/_baserequest.py", line 165, in post result = await self._request_wrapper( File "/Library/Python/3.8/site-packages/telegram/request/_baserequest.py", line 290, in _request_wrapper raise NetworkError(f"Unknown error in HTTP implementation: {repr(exc)}") from exc telegram.error.NetworkError: Unknown error in HTTP implementation: RuntimeError('Event loop is closed')
问题分析
- 线程创建错误:
threading.Thread(target=main())直接执行了main函数,而非将函数作为线程目标传入,导致主线程被阻塞,Bot的run_polling无法正常启动,事件循环未初始化就被调用。正确写法应为target=main,且start需加括号执行。 - 异步上下文冲突:子线程中用
asyncio.run(send_msg())会创建新的事件循环,但Telegram Bot的Application已绑定主线程的事件循环,跨循环调用Bot的异步方法会引发事件循环关闭错误。 - 同步阻塞问题:
time.sleep(5)是同步阻塞操作,会占用子线程,影响任务调度效率。
解决方案
方案一:修正线程逻辑,复用主线程事件循环
import threading from telegram.ext import ApplicationBuilder import asyncio import time botApp = ApplicationBuilder().token(token="******:******").build() def main(): # 计算任务逻辑 while True: # 获取主线程的事件循环,在该循环中执行异步发送 asyncio.run_coroutine_threadsafe(send_msg(), botApp.loop) time.sleep(5) async def send_msg(): print("sending") await botApp.bot.sendMessage( chat_id="********", text='example', parse_mode='markdown' ) if __name__ == '__main__': # 正确创建线程:传入函数对象,调用start()启动 threading.Thread(target=main, daemon=True).start() botApp.run_polling()
方案二:使用Bot内置任务调度(更优雅)
无需手动管理线程,利用Telegram Bot的异步任务调度功能实现定时任务:
from telegram.ext import ApplicationBuilder import asyncio botApp = ApplicationBuilder().token(token="******:******").build() async def compute_and_send(): while True: # 计算任务逻辑 result = "计算结果示例" # 发送消息 print("sending") await botApp.bot.sendMessage( chat_id="********", text=result, parse_mode='markdown' ) # 异步等待5秒,不阻塞事件循环 await asyncio.sleep(5) if __name__ == '__main__': # 创建后台任务 botApp.create_task(compute_and_send()) # 启动Bot botApp.run_polling()
关键说明
- 方案一中使用
asyncio.run_coroutine_threadsafe将异步任务提交到主线程的事件循环中执行,避免跨循环冲突。 - 方案二全程使用异步上下文,无需线程,更符合Telegram Bot的设计规范,避免同步阻塞问题。
- 线程方案中需设置
daemon=True,确保主线程退出时子线程自动终止。
内容的提问来源于stack exchange,提问作者Gab
相关产品推荐
相关产品推荐

