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

Tornado中如何正确关闭包含WebSocket的IOLoop?

解决Tornado多线程关闭时"Task was destroyed but it is pending"错误

问题原因

直接调用io_loop.stop()时,WebSocket连接的清理任务(比如ping/pong握手、连接关闭流程)可能仍处于pending状态,此时强行关闭io_loop会导致未完成的任务被销毁,触发错误。另外add_callback_from_signal仅适用于信号处理场景,在普通线程调用并不合适。

正确关闭方案

需要先优雅关闭WebSocket连接,等待所有相关清理任务完成后再停止io_loop,避免pending任务残留。

修改后的代码

def run(self):
    self.io_loop = tornado.ioloop.IOLoop()
    self.io_loop.make_current()
    self.io_loop.add_callback(self.func)

    self.io_loop.start()
    # 此时所有任务已完成,安全关闭io_loop
    self.io_loop.close()

async def func(self):
    request = tornado.httpclient.HTTPRequest(...)
    self.client = await tornado.websocket.websocket_connect(
        request, 
        on_message_callback=func_msg, 
        ping_interval=num, 
        ping_timeout=num2
    )

def stop(self):
    # 将关闭逻辑提交到对应io_loop的线程执行
    def shutdown_sequence():
        async def close_and_stop():
            if hasattr(self, 'client') and self.client:
                # 等待WebSocket连接关闭流程完成
                await self.client.close()
            # 所有任务处理完毕后停止io_loop
            self.io_loop.stop()
        
        self.io_loop.add_callback(close_and_stop)
    
    self.io_loop.add_callback(shutdown_sequence)

关键说明

  1. 用add_callback替代add_callback_from_signal:后者仅用于信号处理函数,普通线程调用add_callback即可将任务提交到目标io_loop线程执行。
  2. 等待WebSocket关闭完成:client.close()返回一个Future,通过await等待其完成,确保所有连接相关的清理任务执行完毕。
  3. 顺序执行关闭流程:先完成WebSocket的优雅关闭,再停止io_loop,保证io_loop.start()返回时没有pending任务,此时调用io_loop.close()不会报错。

额外注意事项

  • 如果func中的WebSocket连接可能尚未建立完成(self.client为None),需要在关闭逻辑中做判空处理,避免触发属性错误。
  • 多线程环境下,确保调用stop方法时,目标线程的io_loop仍处于运行状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 07:10:03