用不到 60 行标准库代码写一个单线程执行器(任务队列 + Waker + poll 循环 + 定时线程),验证异步运行时做了什么:3 个任务在同一线程交错运行,总耗时约 150 ms 而非 300 ms,poll 6 次;Pending → wake → 重新入队 → 再 poll 的完整路径;任务里 thread::sleep 卡住整个执行器;与 tokio current_thread(150 ms)对照。
async fn 的 Future 是状态机:大小取决于跨 await 保存的局部变量(空 1 字节、跨 await 的 1000 字节数组 1002 字节、3000 字节 3002 字节),而且看作用域而不是最后一次使用(放进内层块只有 2 字节);poll 4 次保存并恢复局部变量;完成后再 poll panic;递归要 Box::pin(E0733);自引用 Future 是 !Unpin(E0277)。
下载 tokio 验证任务调度:4 工作线程运行时上 1000 个任务分布在全部 4 个线程;同一任务的多次 poll 可能换线程(16 个里 12 个);current_thread 里 150 ms 无 await 计算让 ticker 推迟到 150 ms;多线程运行时里 ticker 在 20 ms 就开始;spawn_blocking 不占用运行时线程。
真实验证 tokio::spawn 为什么要求 'static:借用局部变量的 async 块报 E0373;move、Arc、clone 三种修法;tokio::join! 在同一任务里并发可以借用;'static 的含义(拥有的值、字面量、Box::leak 都满足,&local 报 E0597);任务内部的借用不受限制。
下载 tokio 验证 select! 的取消语义:失败的分支被 drop;取消不安全的 Future 会丢消息(通道里剩 [2, 3]);把取走与处理分开才安全;默认随机选择(4910:5090)对 biased 的 10000:0;timeout 超时时内部 Future 被 drop。
下载 tokio 验证四种异步通道:有界 mpsc 满了 send().await 挂起(背压);无界 mpsc 10 万条 15 ms 发完、积压 100000 条;oneshot 的 drop 语义;broadcast 的 Lagged(4);watch 只保留最新值(v1、v2 被跳过)。
下载 tokio 验证异步里的 Mutex 边界:std MutexGuard 跨 await 让 Future 不是 Send(编译器指出 used across an await);tokio::sync::Mutex 可跨 await;guard 不跨 await 时 std Mutex 可用;单线程运行时里跨 await 持 std 锁会死锁(6 秒不退出);无 await 的短临界区 std 75.5 ms 对 tokio 389.7 ms。
下载 tokio 验证阻塞代码混入异步:4 个工作线程时,k = 3 个阻塞任务时 sleep(10 ms) 只晚几毫秒,k = 4 时晚约 260 ms(线程全被占满);spawn_blocking 晚 3 ms;block_in_place 晚 5 ms 但在 current_thread 里 panic;纯 CPU 计算占满 4 线程时 sleep(10 ms) 用了 178 ms。
tokio 验证 Future 取消:drop JoinHandle 不取消、abort() 才取消;abort 只在 await 点生效(同步循环要跑完);取消不回滚副作用(转账丢 40)而「先 await 后一次性修改」安全;tokio Mutex 的 guard 随 Future 释放;Drop 里不能 await(E0728)。
用真实运行验证 Pin 与 Unpin:自引用结构体被移动后 ptr 指向旧地址;Pin<Box<T>> 移动两次后数据地址不变;!Unpin 类型拿不到 &mut(get_mut / Pin::new 报 E0277);逐个检查 Unpin(普通类型是,PhantomPinned 与所有 async 块不是);pin! 宏固定在栈上并 poll 出 Ready(6)。
用带计数的 vtable 验证 Waker:16 字节(数据指针 + vtable);clone / wake_by_ref / wake / drop 各调用哪个函数(wake 消耗 waker 所以不再 drop);Context 里的 &Waker 要 clone 才能保存;will_wake 的结果;忘了调用 wake 时任务永远睡下去。
自己写 Timer Future(共享状态 + 保存 waker + 后台线程唤醒)和 join2 组合子:Timer 等 100 ms poll 2 次;顺序 await 两个 100 ms 耗时约 200 ms 而 join2 约 100 ms;不保存 waker 会永远睡着;waker 会随 poll 变化必须每次更新。