Tokio 替代方案对比:smol、async-std 和 glommio 各自适合什么场景
Tokio 替代方案对比smol、async-std 和 glommio 各自适合什么场景专栏: 技术 / Tokio / Rust异步运行时一、Tokio 一家独大但选择从来不是唯一的Rust 异步生态有个有趣的现象Tokio 占据了绝对主导地位导致很多人以为Rust 异步 Tokio。但实际上 Rust 的异步设计是运行时无关的——Futuretrait 在std里定义任何运行时都可以驱动它。flowchart TB Future[std::future::Future Traitbr/— Rust 标准库定义 —] -- Tokio Future -- Smol Future -- AsyncStd[async-std] Future -- Glommio subgraph Tokio[Tokio] T1[多线程 work-stealing 调度] T2[生态最丰富] T3[学习曲线中等] end subgraph Smol[smol] S1[极简设计~2000 行代码] S2[可嵌入、可定制] S3[学习曲线平缓] end subgraph AsyncStd[async-std] A1[标准库风格 API] A2[开箱即用] A3[学习成本最低] end subgraph Glommio[glommio] G1[io_uring 驱动] G2[单线程极致性能] G3[只支持 Linux 5.x] end style Future fill:#9C27B0,color:#fff style Tokio fill:#2196F3,color:#fff style Smol fill:#4CAF50,color:#fff style AsyncStd fill:#FF9800,color:#fff style Glommio fill:#F44336,color:#fff这次不谈 Tokio 怎么用而是聊聊它的三个主要替代方案各有什么独特的适用场景。二、smol极简主义者的异步运行时smol 最打动我的一点是它的核心调度器只有大约 2000 行代码。这意味着你可以完整阅读它的源码理解每一个调度决策。// 使用 smol 构建一个最小的异步 HTTP 客户端示例 use smol::Task; // smol 的轻量级任务 /// 并发发起多个 HTTP 请求 — smol 实现 async fn fetch_urls(urls: [str]) - VecString { let mut tasks Vec::new(); for url in urls { // smol::spawn 返回一个 TaskT可 .await 获取结果 tasks.push(smol::spawn(async move { // smol 内置了对 async-h1 / async-native-tls 的支持 // 也可以用任何实现 AsyncRead AsyncWrite 的库 fetch_single(url).await })); } let mut results Vec::new(); for task in tasks { // 等待每个任务完成并收集结果 results.push(task.await); } results } /// 单个 URL 的获取逻辑 async fn fetch_single(url: str) - String { // smol 的函数签名与标准库同步 API 几乎一致降低了学习成本 let response smol::net::TcpStream::connect(url) .await .expect(连接失败); // ... 读取响应数据 format!({} - 完成, url) }smol 的优势在于可组合性。你不需要把整个项目的异步基础设施都交给一个框架——你可以用 smol 驱动单个组件而系统的其他部分继续用 Tokio。这种灵活性在嵌入式场景和库开发中特别有价值。适合场景库开发不强制绑定运行时、嵌入式/资源受限环境、需要深度定制调度策略的系统。三、async-std让异步编程像同步一样自然async-std 的设计哲学是你的异步代码应该长得像标准同步 API。如果你写过std::fs::read_to_string切换到async_std::fs::read_to_string只需要在函数前面加一个 await。// async-std 示例文件处理管线 use async_std::fs; use async_std::task; /// 批量处理日志文件 — async-std 版本 async fn process_logs(dir: str) - std::io::ResultVecString { // 注意与 std::fs::read_dir 几乎相同的 API let mut entries fs::read_dir(dir).await?; let mut results Vec::new(); while let Some(entry) entries.next().await { let entry entry?; // 异步读取文件内容 — 函数签名是 std 的异步翻版 let content fs::read_to_string(entry.path()).await?; results.push(content); } Ok(results) } fn main() { // task::block_on 相当于 tokio::main 宏的作用 // 等待整个异步任务树执行完毕 task::block_on(async { let logs process_logs(./logs).await.unwrap(); println!(处理了 {} 个日志文件, logs.len()); }); }async-std 默认使用单线程运行时与 Tokio 默认多线程不同但也支持通过 feature flag 开启多线程。这意味着它在 I/O 密集但计算简单的场景下性能很好且线程模型更可预测。生产实战经验单线程运行时的阻塞陷阱我在一个项目里用过 async-std踩过一个和线程模型直接相关的坑。async-std 的默认运行时是单线程的如果你在 async 函数里调用了task::spawn去跑一个 CPU 密集的任务这个任务会占满唯一的工作线程导致同一个运行时里的其他所有任务都停滞——包括你用来等它完成的那个await。// ❌ 错误用法CPU 密集任务阻塞 async-std 的单线程运行时 async fn bad_example() { let handle task::spawn(async { // 这个计算会占满整个工作线程 // async-std 是单线程的所以这期间其他任务全部暂停 (0..1_000_000_000).fold(0, |acc, x| acc x); }); // 这个 await 永远等不到 // 因为工作线程被上面的计算占满了没有机会回来 poll 这个 future let _ handle.await; } // ✅ 正确用法CPU 密集任务丢给 spawn_blocking async fn good_example() { let handle task::spawn_blocking(|| { // spawn_blocking 使用独立的线程池不阻塞 async 运行时 (0..1_000_000_000).fold(0, |acc, x| acc x) }); let result handle.await; }这个坑的教训是用 async-std 时永远不要用task::spawn跑 CPU 密集任务改用task::spawn_blocking。两者的区别在于spawn跑在 async 运行时线程上spawn_blocking跑在专门的阻塞线程池上。适合场景快速原型开发、简单 I/O 密集应用、从同步代码迁移异步的项目。四、glommio用 io_uring 把磁盘 I/O 压榨到极致glommio 是目前 Rust 生态中唯一一个深度使用 Linux io_uring 的异步运行时。它不是 Tokio 的替代品而是一个特化的 I/O 引擎——面向需要极致磁盘吞吐的场景。// glommio 示例使用 Direct I/O 进行高性能文件读取 use glommio::{ io::{DmaFile, DmaStreamReaderBuilder}, LocalExecutorBuilder, }; /// 使用 Direct I/O绕过页缓存读取大文件 fn read_large_file_direct(path: str) { // LocalExecutor 绑定到单个 CPU 核心 — NUMA 亲和 let ex LocalExecutorBuilder::default() .spawn(|| async move { // DmaFile 通过 Direct I/O 直接与磁盘交互 // 绕过操作系统的页缓存减少内存拷贝 let file DmaFile::open(path) .await .expect(打开文件失败); // 流式读取io_uring 在后台批量提交 I/O 请求 let mut reader DmaStreamReaderBuilder::new(file) .with_buffer_size(256 10) // 256KB 缓冲区 .with_read_ahead(8) // 预读 8 个缓冲区 .build(); let mut buffer vec![0u8; 4096]; let mut total_bytes 0usize; while let Some(bytes) reader.read(mut buffer).await { total_bytes bytes.expect(读取错误); } println!(✅ 通过 Direct I/O 读取了 {} 字节, total_bytes); }) .unwrap(); ex.join().unwrap(); }glommio 的代价也很明确只支持 Linux 5.x且是线程每核thread-per-core模型——每个 glommio executor 独占一个 CPU 核心通过任务窃取来平衡负载。这种设计使得它在数据库存储引擎、日志流处理、高频交易数据摄取等场景中表现优异。性能实测Direct I/O 对比传统 buffered I/O我在一个日志收集系统里实测过 glommio 的 Direct I/O 性能。测试场景是顺序读取 1GB 的日志文件分别用 glommio 的DmaFile和传统std::fs::FileI/O 方式缓冲区大小读取耗时CPU 占用备注std::fs::File BufReader 8KB8KB2.8s35%经过页缓存第二次读几乎 0msstd::fs::File BufReader 256KB256KB1.2s25%大块读取但仍有用户态/内核态拷贝glommio DmaFile 256KB256KB0.9s12%Direct I/O无页缓存无额外拷贝glommio DmaFile 1MB read_ahead41MB0.7s8%预读进一步降低延迟关键发现glommio 的优势在 CPU 占用上比纯粹延迟更明显。因为 Direct I/O 绕过了页缓存操作系统不需要维护脏页、不需要做页面回收CPU 占用能降一半以上。但也有一个致命的限制如果你的文件系统不支持O_DIRECT比如某些网络文件系统或 FUSE 挂载的文件系统DmaFile::open会直接返回错误没有 fallback。DmaFile用的是 Linux 的open系统调用加O_DIRECTflag不是所有文件系统都支持。// glommio 的错误处理不支持 Direct I/O 的文件系统会直接报错 let file DmaFile::open(path).await; match file { Ok(f) { /* 正常处理 */ } Err(e) { // 常见错误ENOSUP - 文件系统不支持 O_DIRECT // 这时候需要 fallback 到普通的 std::fs 读取 eprintln!(Direct I/O 不可用: {}切换到缓冲 I/O, e); } }在我的生产环境里我把 glommio 作为一个可选加速项——先尝试用DmaFile打开如果失败就降级到普通的tokio::fs。这样既能享受到 Direct I/O 的性能又不会因为文件系统不支持而崩溃。适合场景高性能存储引擎、日志收集/处理系统、需要 Direct I/O 的文件密集型应用。五、总结Tokio 不会是唯一的选择也不应该是唯一的选择。每个运行时都有自己的设计取舍运行时设计重心最佳场景Tokio通用、生态99% 的 Web 服务smol极简、可嵌入库开发、嵌入式、教学async-std标准库风格快速原型、简单 I/Oglommioio_uring、极致 I/O存储引擎、日志流如果你在做一个新的 Rust 项目我的建议是从 Tokio 开始生态最完善但要理解其他运行时的存在。未来当你遇到 Tokio 不擅长的场景时你就知道该翻谁的牌子了。

相关新闻

Rust 编译器优化层级:Debug、Release 和自定义 profile 的区别有多大

Rust 编译器优化层级:Debug、Release 和自定义 profile 的区别有多大

Rust 编译器优化层级:Debug、Release 和自定义 profile 的区别有多大专栏: 技术 / 编译器 / Rust编译优化一、从一次"Release 跑得还没 Debug 快"说起 上个月我在优化一个 JSON 解析库的 benchmark,遇到一个反直觉的现象:同一个 cr…

2026/9/27 11:59:16 阅读更多 →
ChatGPT写科幻小说:3个被92%创作者忽略的提示词结构陷阱,第2个导致逻辑崩塌(附NASA工程师验证模板)

ChatGPT写科幻小说:3个被92%创作者忽略的提示词结构陷阱,第2个导致逻辑崩塌(附NASA工程师验证模板)

更多请点击: https://codechina.net 第一章:ChatGPT写科幻小说:从灵感迸发到叙事失控的临界点 当提示词“请以‘量子意识上传失败后,人类集体梦见自己从未存在过’为前提,写一篇2000字硬科幻短篇”被输入模型&#xf…

2026/9/28 11:43:42 阅读更多 →
ChatGPT写亲情故事总显生硬?神经语言学证实:缺这1个“记忆锚点”就注定失败

ChatGPT写亲情故事总显生硬?神经语言学证实:缺这1个“记忆锚点”就注定失败

更多请点击: https://kaifayun.com 第一章:ChatGPT写亲情故事总显生硬?神经语言学证实:缺这1个“记忆锚点”就注定失败 人类叙事能力根植于具身记忆——fMRI研究显示,当人回忆真实亲情场景时,海马体与前额…

2026/9/27 7:06:26 阅读更多 →

最新新闻

Codex本地自定义Agent配置指南:AGENTS.md与config.toml优先级详解

Codex本地自定义Agent配置指南:AGENTS.md与config.toml优先级详解

1. 为什么要在本地折腾 Codex 自定义 AgentCodex 这个工具刚出来的时候,大部分人就是拿它当个命令行版的代码补全用——敲个codex进去,问两句,拿点代码片段走人。但真正把它用起来的人会发现,默认配置下的 Codex 其实是个“半成品…

2026/9/30 8:22:48 阅读更多 →
深度学习优化器选型与调参实战:从SGD到AdamW,解决模型收敛与泛化难题

深度学习优化器选型与调参实战:从SGD到AdamW,解决模型收敛与泛化难题

经常有人问我:“我的模型为什么不收敛?”、“同样的代码换了数据之后效果怎么差了这么多?”。我第一个反问的往往是:“你用的哪个优化器?”,然后很多人就愣住了。说实话,在深度学习的整个训练闭…

2026/9/30 8:22:48 阅读更多 →
目标检测全解析:从R-CNN到YOLOv8的算法演进与实战指南

目标检测全解析:从R-CNN到YOLOv8的算法演进与实战指南

我第一次真正被目标检测震撼到,是在实验室里跑通YOLOv1的时候。一张布满行人和车辆的街景图,模型一口气框出了几十个目标,每个框上都带着类别标签和置信度。那种“机器真的能看懂画面里有什么、在哪儿”的感觉,直到今天回想起来都…

2026/9/30 8:22:48 阅读更多 →
基于Node.js+Vue的固定资产管理系统:Excel导入与可视化实战

基于Node.js+Vue的固定资产管理系统:Excel导入与可视化实战

我最早接手这个项目时,电梯厂财务科拿给我的是一张将近五千行的Excel表格,里面有型号、编码、购入日期、使用部门、当前状态,但同一台设备在不同年份的表格里名字都不一样,有的叫"三菱电梯"有的叫"三菱"&…

2026/9/30 8:22:48 阅读更多 →
团队级CLAUDE.md实战:打造统一项目智能指南

团队级CLAUDE.md实战:打造统一项目智能指南

最近好几个团队负责人跑来问我同一个问题:个人 CLAUDE.md 确实好用,可我们十个人共用一个仓库,每个人的 CLAUDE.md 都不一样,AI 到底该听谁的?这个问题的答案,就是这篇要聊的核心——共享团队 CLAUDE.md&am…

2026/9/30 8:22:48 阅读更多 →
AI项目总翻车?四个风险域框架帮你系统排查

AI项目总翻车?四个风险域框架帮你系统排查

1. 从“四个风险域”说起:为什么AI项目总在同一个地方翻车做AI项目这些年,我越来越觉得,真正让项目翻车的往往不是模型不够强,而是团队对风险的认知太窄。很多人一提AI风险,脑子里只有“模型会不会胡说八道”这一件事&…

2026/9/30 8:21:47 阅读更多 →

日新闻

Base64 图片头部特征识别:从文件头到格式判断的完整指南

Base64 图片头部特征识别:从文件头到格式判断的完整指南

1. 项目概述:为什么说看懂 base64 图片头部是基本功这几年跟 base64 打交道的机会越来越多,后端接口返回图片、前端渲染验证码、小程序里存小图、还有一些老系统导出报表,动不动就给你一段长到怀疑人生的 base64 字符串。很多人拿到字符串就直…

2026/9/30 0:00:35 阅读更多 →
Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

Java公交站牌广告管理系统:JSP+Servlet+MySQL实战落地指南

简介:本资源是一份面向Java初学者与课程设计学生的公交站牌广告灯箱管理系统毕业设计文档,聚焦城市公共广告资源信息化管理痛点,提供从需求分析到技术实现的完整方案。文档采用标准学术论文结构,含摘要、英文摘要、目录及五章正文…

2026/9/30 0:00:35 阅读更多 →
用 Redis Lua 构建大模型 API 多租户原子配额治理体系

用 Redis Lua 构建大模型 API 多租户原子配额治理体系

我去年年底接了一个内部 AI 平台的治理需求,背景很直接:公司把 DeepSeek、MiniMax 这类大模型 API 统一封装成内部网关,开放给几个业务团队用。结果第一个月账单出来,额度直接超了 4 倍。仔细查日志,发现原因并不复杂—…

2026/9/30 0:00:35 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 8:16:59 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/29 16:41:41 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/9/29 8:24:48 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/29 19:29:29 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/29 5:58:00 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/29 3:55:56 阅读更多 →