DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS

Dask Client.close 报 Cannot synchronously wait:核对 Bokeh 关闭接口

distributed 2026.7.1 搭配 Bokeh 3.10 时,dashboard 的同步关闭可能抛 RuntimeError。2026.8.0 已等待 stop_async;暂不能升级时可关闭本地 dashboard。

Dask 任务已经跑完,关闭本地客户端时却出现下面的错误,异常栈经过 Scheduler.close()、stop_services() 和 Bokeh 的 stop():

RuntimeError: Cannot synchronously wait on a running event loop; use 'await stop_async()'

已知触发组合是 distributed 2026.7.1 与 Bokeh 3.10.0。这里出错的是本地集群的 dashboard 关闭过程:调度器正在异步关闭服务,却调用了要求同步等待的 Bokeh 接口。Bokeh 3.10.1 也保留了这项事件循环保护,单独更新 Bokeh 不会让旧调用变成异步等待。

优先修正 distributed 的关闭路径

distributed 2026.8.0 的正式实现已提供异步服务关闭方法:应用有 stop_async() 时等待它,旧接口才退回 stop();调度器关闭时也会等待这个异步方法。需要保留 dashboard 的环境,应评估升级 distributed,并按项目依赖约束同步核对 Dask 的配套版本。

这项变更不能修复任务本身的异常、worker 失联或任意关闭超时。先看原异常栈是否确实落在 Bokeh 的同步 stop(),再决定是否属于这一路径。

普通同步脚本可以继续通过客户端上下文管理本地资源,无需给业务入口额外创建事件循环。下面只是资源管理示例,实际任务放在上下文内:

from distributed import Client

with Client(processes=False) as client:
    results = client.gather(client.map(abs, [-2, 3]))

Client 自行创建本地集群时会负责关闭它。连接已有远程调度器的客户端不拥有整个远端集群,关闭本地连接与关闭远端服务需要分别处理。

暂时不能升级,可以关闭本地 dashboard

如果任务不依赖浏览器监控页,创建本地集群时可明确关闭调度器 dashboard,避开这项服务的启动与停止:

from distributed import Client

with Client(processes=False, dashboard_address=None) as client:
    results = client.gather(client.map(abs, [-2, 3]))

这会失去该本地集群的 dashboard,不会关闭任务执行能力。参数要传给实际创建集群的入口;若项目先创建了 LocalCluster,应在创建它时配置。已经运行的远程调度器也不会因为客户端带了这个参数,就按本地集群的方式重新配置。

不要在外层捕获所有 RuntimeError 后直接忽略,也不要关闭调度器正在使用的事件循环。前者会一起吞掉其他清理失败,后者可能让尚未关闭的连接和任务失去清理机会。Bokeh 旧版可能没有同样的拒绝逻辑,但降级整个监控依赖并不能补上 distributed 的异步关闭调用。

检查时要从原来的资源所有者退出:本地创建客户端的程序,应确认退出不再抛错,dashboard 端口与其拥有的 worker 都已释放;使用 pytest fixture 的项目,还要让 teardown 真正执行。测试主体通过而 teardown 失败,仍是一次失败运行。若使用临时关闭 dashboard 的方案,验收后也应保留这项功能取舍,避免把监控页消失误认成另一个故障。