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

如何优雅退出WebSocket与asyncio事件循环示例?避免任务报错

解决WebSocket服务器优雅退出问题

我需要实现当buttcount达到3时,干净停止WebSocket服务器并退出程序,无错误信息。尝试用asyncio.get_event_loop().stop()时,出现大量未完成任务被销毁的报错。

原代码

#!/usr/bin/env python

import asyncio
import websockets

buttcount=0

async def hello(websocket, path):
    global buttcount
    name = await websocket.recv()
    print(name)
    if name=="butt":
        buttcount += 1
    if buttcount == 3:
        #Do something really cool to gracefully exit without error messages    

start_server = websockets.serve(hello, "localhost", 8765)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

报错信息

Task was destroyed but it is pending!

task: <Task pending name='Task-4' coro=<WebSocketServerProtocol.handler() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/server.py:244> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08a60>()]>>

Task was destroyed but it is pending!

task: <Task pending name='Task-5' coro=<WebSocketCommonProtocol.transfer_data() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:968> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08b20>()]> cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08e80>()]>>

Task was destroyed but it is pending!

task: <Task pending name='Task-6' coro=<WebSocketCommonProtocol.keepalive_ping() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:1259> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08eb0>()]>>

Task was destroyed but it is pending!

task: <Task pending name='Task-7' coro=<WebSocketCommonProtocol.close_connection() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:1314> wait_for=<Task pending name='Task-5' coro=<WebSocketCommonProtocol.transfer_data() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:968> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08b20>()]> cb=[<TaskWakeupMethWrapper object at 0x7f74e6d08e80>()]>>

Task was destroyed but it is pending!

task: <Task pending name='Task-8' coro=<WebSocketCommonProtocol.write_close_frame() running at /usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py:1216> cb=[_release_waiter(<Future pendi...4e6d08a60>()>)() at /usr/lib/python3.8/asyncio/tasks.py:429]>

sys:1: RuntimeWarning: coroutine 'WebSocketCommonProtocol.write_close_frame' was never awaited

Exception ignored in: <coroutine object WebSocketCommonProtocol.close_connection at 0x7f74e6ce6740>

Traceback (most recent call last):

  File "/usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py", line 1354, in close_connection

  File "/usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py", line 1372, in close_transport

  File "/usr/local/lib/python3.8/dist-packages/websockets/legacy/protocol.py", line 1397, in wait_for_connection_lost

  File "/usr/lib/python3.8/asyncio/tasks.py", line 448, in wait_for

解决方案

要优雅退出,需要先关闭服务器停止接受新连接,等待现有连接处理完成,再停止事件循环。修改后的代码如下:

#!/usr/bin/env python

import asyncio
import websockets

buttcount = 0
server = None  # 保存服务器实例

async def hello(websocket, path):
    global buttcount, server
    name = await websocket.recv()
    print(name)
    if name == "butt":
        buttcount += 1
    if buttcount == 3:
        # 关闭服务器,停止接受新连接
        server.close()
        # 等待服务器完全关闭
        await server.wait_closed()
        # 获取事件循环并停止
        loop = asyncio.get_running_loop()
        loop.stop()

async def main():
    global server
    server = await websockets.serve(hello, "localhost", 8765)
    await server.wait_closed()  # 等待服务器关闭信号

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    # 清理剩余的任务
    pending = asyncio.all_tasks(loop)
    loop.run_until_complete(asyncio.gather(*pending))
    loop.close()

关键修改点

  • 保存websockets.serve()返回的服务器实例,用于后续关闭操作
  • 当buttcount达到3时,先调用server.close()停止接受新连接
  • 用await server.wait_closed()等待服务器完成关闭流程
  • 使用asyncio.get_running_loop().stop()停止事件循环
  • 主函数中先运行服务器,等待关闭后再清理所有未完成任务,最后关闭事件循环

这样能确保所有WebSocket相关任务都正常完成,不会出现未完成任务被销毁的报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 21:31:01