背景

spawn 后丢掉 handle,会让调用方失去等待、错误检查和取消入口。后台任务应该有生命周期所有者,而不是依靠进程退出兜底。

核心判断

  1. 单任务保存 JoinHandle,多任务使用 JoinSet 集中回收。
  2. 区分业务 Result 与 JoinError 中的 panic、取消。
  3. 优先协作停止,必要时 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 和取消,不让后台任务失去所有者。