Tokio 任务所有权:每个 spawn 都应该有回收策略
背景 spawn 后丢掉 handle,会让调用方失去等待、错误检查和取消入口。后台任务应该有生命周期所有者,而不是依靠进程退出兜底。 核心判断 单任务保存 JoinHandle,多任务使用 JoinSet 集中回收。 区分业务 Result 与 JoinError 中的 panic、取消。 优先协作停止,必要时 abort 后继续 await 回收结果。 最小示例 下面保留核心逻辑;业务类型、调用入口和环境配置按项目补齐。 let handle = tokio::spawn(async move { worker.run(shutdown).await }); match handle.await { Ok(Ok(())) => tracing::info!("worker stopped"), Ok(Err(err)) => tracing::error!(error = %err, "worker failed"), Err(err) => tracing::error!(error = %err, "worker join failed"), } 边界与误区 丢弃 JoinHandle 会分离任务,并不等于停止任务。worker 和 shutdown 是业务对象,示例只展示持有 handle 和等待的边界。 验证方式 让任务正常返回、失败、panic 和被取消各一次,确认监督者都有处理路径;服务停止时检查没有孤立后台任务继续访问资源。 小结 保存 JoinHandle,集中处理任务完成、错误、panic 和取消,不让后台任务失去所有者。