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)
关键说明
- 用
add_callback替代add_callback_from_signal:后者仅用于信号处理函数,普通线程调用add_callback即可将任务提交到目标io_loop线程执行。 - 等待WebSocket关闭完成:
client.close()返回一个Future,通过await等待其完成,确保所有连接相关的清理任务执行完毕。 - 顺序执行关闭流程:先完成WebSocket的优雅关闭,再停止io_loop,保证
io_loop.start()返回时没有pending任务,此时调用io_loop.close()不会报错。
额外注意事项
- 如果
func中的WebSocket连接可能尚未建立完成(self.client为None),需要在关闭逻辑中做判空处理,避免触发属性错误。 - 多线程环境下,确保调用
stop方法时,目标线程的io_loop仍处于运行状态。
内容的提问来源于stack exchange,提问作者Hirisar
相关产品推荐
相关产品推荐

