Rust中Future需await才执行,为何未await的tokio::spawn仍运行?
Rust异步执行现象底层原因
首先纠正一个认知边界:Future只有被await才会执行的结论,仅适用于没有被提交到异步运行时调度队列的普通Future。一旦调用tokio::task::spawn,执行逻辑就会发生本质变化,两个异常场景的具体原因如下:
场景1:未await JoinHandle仍能输出第二条打印
核心原因有两点:
task::spawn的本质是把传入的async块(即一个Future实例)直接提交给Tokio运行时的全局调度队列,JoinHandle只是用来获取任务返回值、感知任务状态的句柄,不是任务执行的开关。只要任务被提交到运行时,不需要主动await JoinHandle,运行时的工作线程有空就会主动轮询、调度这个任务执行,这是spawn生成的托管任务和普通惰性Future的核心区别。- 你在spawn的async块中写
produce_event(500)时没有加.await,这行代码只会临时创建一个produce_event返回的Future实例,随即就会被丢弃,produce_event函数体内的逻辑一行都不会执行。整个spawn的async块中不存在任何会让出执行权的.await点,当运行时第一次轮询到这个任务时,会同步执行完块内所有逻辑,直接走到println!("Event produced at {}", seconds)完成打印,整个过程没有任何等待,执行速度极快,哪怕main函数即将退出,也大概率能在进程终止前跑完这段同步逻辑。
场景2:给produce_event(500)加.await后仅输出第一条打印
核心原因是任务执行到休眠点就被运行时挂起,还没等唤醒执行后续逻辑,整个进程就已经退出了:
- 加上
.await后,spawn的任务被运行时第一次轮询时,会真正进入produce_event函数执行,首先打印出第一条waiting 500的日志。 - 紧接着代码执行到
time::sleep(...).await,此时任务会立刻让出执行权,通知运行时:"500纳秒后再唤醒我继续执行",任务本身被挂起等待。 - 你的
f函数只是把spawn生成的JoinHandle存在了局部Vec中,从来没有await这些JoinHandle等待任务完成,f函数在提交完所有spawn任务后就会直接返回。main函数中通过FuturesUnordered等待所有f执行完成后,就会直接终止main函数、关闭Tokio运行时,所有未执行完成的spawn任务会被直接销毁。 - 500纳秒的休眠时间极短,但任务被休眠到期唤醒、重新调度到工作线程执行的开销,远大于进程直接退出的速度,因此挂起的任务根本没机会执行到后续的第二条打印逻辑,进程就已经终止了。
补充说明:你提到的末尾等待所有Future执行完成后两条日志都正常输出的现象,本质是因为await JoinHandle会把当前执行流程卡住,直到spawn的任务被唤醒、执行完所有逻辑,给任务留足了完整执行的时间,不会被运行时提前销毁。
内容的提问来源于stack exchange,提问作者alexik96
相关产品推荐
相关产品推荐

