功能说明
示例用 tokio::sync::Notify 广播关闭信号,三个工作循环在 tokio::select! 中同时等待通知和 25 毫秒的定时 tick。主任务运行 60 毫秒后调用 notify_waiters(),所有正在等待的工作者被唤醒,跳出循环,执行 20 毫秒的清理,然后由 JoinSet 逐个 join,最后打印关闭完成。
关键代码
tokio::select! {
_ = shutdown.notified() => {
println!("worker {worker} draining");
break;
}
_ = sleep(Duration::from_millis(25)) => {
println!("worker {worker} tick");
}
}
...
shutdown.notify_waiters();
while let Some(result) = tasks.join_next().await {
result.expect("worker panicked");
}
notify_waiters 只唤醒当前已注册的等待者,因此必须在工作循环已经开始轮询之后再调用。
使用注意
Notify不保存通知状态;如果某个工作者在通知之后才首次轮询,它会错过信号并继续运行。- 示例用固定 60 毫秒确保所有工作者已进入 select,生产环境应结合启动屏障或
watch通道保存状态。 - 清理阶段仍是异步的,主任务通过
join_next等待它完成,不会提前退出。 - 任务 panic 会在
join_next返回Err,示例用expect让问题显式暴露。 - 真实服务还应处理强制超时,避免某个任务卡住导致进程无法退出。
验证方式
运行示例会看到多次 tick,随后三个工作者分别打印 draining 与 stopped,最后输出 shutdown complete。把主任务睡眠改短可能让部分工作者错过通知,可据此理解 Notify 不保存状态的限制。