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

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')
问题分析
  1. 线程创建错误:threading.Thread(target=main())直接执行了main函数,而非将函数作为线程目标传入,导致主线程被阻塞,Bot的run_polling无法正常启动,事件循环未初始化就被调用。正确写法应为target=main,且start需加括号执行。
  2. 异步上下文冲突:子线程中用asyncio.run(send_msg())会创建新的事件循环,但Telegram Bot的Application已绑定主线程的事件循环,跨循环调用Bot的异步方法会引发事件循环关闭错误。
  3. 同步阻塞问题: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 09:30:58