基于Future(MVP),用于为外部提供基础的通用异步运行时和工具
主要特征
- 任务池: 可定制的任务池
- 任务ID: 外部使用任务ID可以很方便的唤醒和挂起
- 抽象接口: 可以自由实现自己的运行时
- 运行时推动:单线程运行时可以用自己的方式推动运行
Examples
本地异步运行时:
use pi_async_rt::rt::{AsyncRuntime, AsyncRuntimeExt, serial_local_thread::{LocalTaskRunner, LocalTaskRuntime}};
let rt = LocalTaskRunner::<()>::new().into_local();
let _ = rt.block_on(async move {});
多线程异步运行时使用:
use pi_async_rt::rt::{AsyncRuntime, AsyncRuntimeExt};
use pi_async_rt::rt::multi_thread::{MultiTaskRuntime, MultiTaskRuntimeBuilder, StealableTaskPool};
let pool = StealableTaskPool::with(4,100000,[1, 254],3000);
let builer = MultiTaskRuntimeBuilder::new(pool)
.set_timer_interval(1)
.init_worker_size(4)
.set_worker_limit(4, 4);
let rt = builer.build();
let _ = rt.spawn(async move {});
timeout 等待句柄
runtime.timeout(ms).await 使用 timeout 专用等待句柄,不再为每次 timeout 分配普通任务 TaskId/TaskHandle。该实现保持公开 API 不变,并保留当前 timer 不支持取消的语义:
- timeout 到期后唤醒等待任务,并在 future 完成后释放等待句柄。
- timeout future 被提前 drop 时会清理 waker,timer 到期后释放内部等待句柄。
- 不修改普通
spawn、spawn_timing、任务池和 worker loop 的核心语义。 - 内部等待状态使用原子到期标记和
AtomicWaker,不引入自旋等待或阻塞锁。
建议验证命令:
cargo test --lib timeout_waiter_tests
cargo test --test timeout_waiter
cargo test --test timeout_waiter test_multi_thread_timeout_churn_rss_diagnostic -- --ignored --nocapture
cargo test --features serial --test timeout_waiter
cargo bench --bench timeout_waiter_pi_async -- --nocapture
AsyncValue
AsyncValue<V> 是同步非阻塞、只允许设置一次的 single-shot future。当前实现保持公开 API 不变:
AsyncValue::new()、AsyncValue::set(self, value)和Future<Output = V>签名不变。set()保持旧语义:第一次设置成功,后续设置静默失败且不会覆盖已设置值。- pending 后允许重复 poll,重复 poll 会更新最新 waker,不会 panic。
- set 后唤醒最新 waker;never set 的 future 会继续 pending。
- 当前 API 不表达 sender/receiver 拆分、关闭或取消语义。
建议验证命令:
cargo test --test async_value -- --nocapture --test-threads=1
cargo test --features serial --test async_value -- --nocapture --test-threads=1
cargo bench --bench async_value_pi_async -- --nocapture
cargo bench --features serial --bench async_value_pi_async -- --nocapture
本地专项基准样例(WSL2 Ubuntu 22.04):
| 场景 | 样例结果 | 折算指标 |
|---|---|---|
| default pending poll | 4.57 ns/iter | 约 218.82M polls/s |
| default set then ready | 26.92 ns/iter | 约 37.15M ops/s |
| default single-thread runtime 内部 set/await | 147,983.84 ns/iter,128 pairs/iter | 约 864,959 pairs/s |
| default multi-thread runtime 外部 set/await | 651,453.15 ns/iter,256 pairs/iter | 约 392,968 pairs/s |
| default multi-thread runtime 内部 set/await | 952,500.35 ns/iter,256 pairs/iter | 约 268,766 pairs/s |
| default single-thread wait + multi-thread set 跨 runtime | 1,150,093.09 ns/iter,64 pairs/iter | 约 55,648 pairs/s |
| serial pending poll | 4.56 ns/iter | 约 219.30M polls/s |
| serial set then ready | 25.20 ns/iter | 约 39.68M ops/s |
| serial single-thread runtime 内部 set/await | 125,086.93 ns/iter,128 pairs/iter | 约 1.023M pairs/s |
worker wake/sleep 唤醒协议
多线程 runtime 的 worker 空闲休眠路径会在任务入队或任务 waker 被外部线程触发后即时唤醒 sleeping worker,不再依赖 worker_sleep_timeout 超时兜底。该修复保持公开 API 不变:
- 不修改
AsyncRuntimetrait。 - 不修改
spawn、spawn_local、timeout、yield_now的函数签名和返回语义。 - 每次入队或 wake 最多唤醒一个 worker,避免广播式唤醒风暴。
- worker 休眠注册和外部唤醒使用同一个 condvar predicate,避免 “任务已入队但 worker 继续睡到 timeout” 的 lost wake。
- waits 队列使用有限扫描和 stale entry 清理,不进行无界循环。
- direct worker thread 和 serial worker thread 同步使用二次检查协议。
AsyncRuntimeBuilder::default_multi_thread(..., Some(0), ...)保持原有 builder fallback 语义,不会构建 0 worker pool;Some(n > 0)仍创建同尺寸StealableTaskPool,避免 worker 数大于 pool slot。
建议验证命令:
cargo test --lib worker_waker -- --nocapture --test-threads=1
cargo test --test worker_wakeup -- --nocapture --test-threads=1
cargo test --features serial --lib worker_waker -- --nocapture --test-threads=1
cargo bench --bench worker_wakeup_pi_async -- --nocapture
本地专项基准样例(WSL2 Ubuntu 22.04,8 worker):
AsyncValue外部 wake:p50 66.705us,p99 137.103us,max 169.398us。- 外部
spawn:p50 65.402us,p99 153.818us,max 213.274us。 - 8 个外部 producer 并发
spawn到 8 worker runtime,1,000,000 个空任务:约 7,139,442 tasks/sec。 - 百万并发空任务资源观测:最大 RSS 80,236 KiB,Swaps 0。
基准测试
云服务平台
- 16核(vCPU) 2.5 GHz主频、3.2 GHz睿频的Intel ® Xeon ® Platinum 8269CY(Cascade Lake)
- 内存:64G
- CentOS 7.3 64位
| 项目 | pi_async | async_std | tokio | 备注 |
|---|---|---|---|---|
| bench_async_mutex | 3,266 ns/iter (+/- 136) | 149,332 ns/iter (+/- 7,212) | 6,374,238 ns/iter (+/- 861,432) | |
| contention | 338,786 ns/iter (+/- 68,222) | 901,779 ns/iter (+/- 28,380) | 2,157,495 ns/iter (+/- 38,100) | |
| create | 257 ns/iter (+/- 2) | 61 ns/iter (+/- 0) | 63 ns/iter (+/- 0) | |
| no_contention | 215,515 ns/iter (+/- 1,121) | 225,034 ns/iter (+/- 740) | 550,285 ns/iter (+/- 2,313) | |
| await_empty_many | 605,232 ns/iter (+/- 125,354) | 394,823 ns/iter (+/- 19,107) | 393,625 ns/iter (+/- 4,459) | |
| chained_spawn | 504,570 ns/iter (+/- 24,166) | 1,090,176 ns/iter (+/- 27,817) | 251,943 ns/iter (+/- 1,412) | |
| ping_pong | 1,176,361 ns/iter (+/- 197,786) | 3,859,845 ns/iter (+/- 73,410) | 1,193,711 ns/iter (+/- 20,376) | |
| spawn_empty_many | 4,187,949 ns/iter (+/- 587,053) | 18,887,015 ns/iter (+/- 347,589) | 9,941,412 ns/iter (+/- 659,722) | |
| spawn_many | 3,436,761 ns/iter (+/- 279,137) | 19,001,495 ns/iter (+/- 380,355) | 7,615,952 ns/iter (+/- 210,639) | |
| spawn_one_to_one | 6,205,756 ns/iter (+/- 826,745) | 36,189,628 ns/iter (+/- 357,690) | 16,620,075 ns/iter (+/- 589,085) | |
| yield_many | 23,757,528 ns/iter (+/- 4,110,213) | 52,304,694 ns/iter (+/- 519,928) | 17,746,497 ns/iter (+/- 550,878) | |
| block_on | 83 ns/iter (+/- 0) | 2,593 ns/iter (+/- 48) | 178 ns/iter (+/- 1) | |
| local_run | 666,627 ns/iter (+/- 5,476) | |||
| local_send_many | 5,885,537 ns/iter (+/- 98,251) | |||
| local_spawn_many | 1,260,102 ns/iter (+/- 5,423) | 20,201,034 ns/iter (+/- 692,642) | 1,553,246 ns/iter (+/- 49,815) |
贡献指南
License
This project is licensed under the MIT license.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in pi_async by you, shall be licensed as MIT, without any additional terms or conditions.