bigywater.tech
Rust · 105

Future 取消与 Drop

Rust 的 async 取消没有 finally:Future 被 drop 时,执行到哪一行就停在哪一行。这篇验证取消到底发生在什么时候、之前的修改会不会回滚、锁会不会泄漏、异步清理该放在哪。

不取消
仅 drop JoinHandle 时任务的命运(继续在后台运行)
丢 40
分两步转账被 timeout 取消后账户合计少了多少
E0728
在 Drop 里 await 的编译错误

JoinHandle 与 abort

实测 · 本机执行输出
    任务 B abort 之后 await:Err("JoinError::Cancelled")
    任务 A 跑完了(JoinHandle 早就被 drop 了)

Future 与 drop 的基础见Future 与 Poll。

取消只发生在 await 点

实测 · 本机执行输出
    [  0 ms] 任务开始 200 ms 的同步循环(没有 await)
    [ 50 ms] 主任务调用 h.abort()
    [200 ms] 同步循环结束,即将进入第一个 await
    [200 ms] h.await 返回 Err(true)

副作用不会回滚

实测 · 本机执行输出
    transfer 被 timeout 取消:true  账户 A = 60,账户 B = 0,合计 60(转账前合计 100)
    换新账户,先 await 后一次性改两个账户:被取消 true  A = 100,B = 0,合计 100(取消时什么都没改,没有损失)

select! 里的取消安全问题见select! 取消竞争。

锁的释放

实测 · 本机执行输出
    持锁的任务被 timeout 取消:true
    之后立即 try_lock: 成功(锁已随 Future 释放)

Drop 里不能 await

实测 · 本机执行输出
  error[E0728]: `await` is only allowed inside `async` functions and blocks
   --> src/main.rs:2:99
    |
  2 | impl Drop for Conn { fn drop(&mut self) { tokio::time::sleep(std::time::Duration::from_millis(1)).await; } }
    |                                                                                                   ^^^^^ only allowed inside `async` functions and blocks
  For more information about this error, try `rustc --explain E0728`.
  error: could not compile `u105e` (bin "u105e") due to 1 previous error

分步演示

Future 取消与 Drop
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。tokio 1.x
    • 没有验证:tokio_util 的 CancellationToken 与 DropGuard、spawn 的任务 panic 时的传播、AsyncDrop(不稳定)、数据库事务在 Future 被取消时的实际行为、JoinSet 被 drop 时的取消行为
    京ICP备2026067502号-1京公网安备11010502063487号