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/10/20 6:37:41 阅读更多 →
ChatGPT写科幻小说:3个被92%创作者忽略的提示词结构陷阱,第2个导致逻辑崩塌(附NASA工程师验证模板)

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

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

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

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

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

2026/10/19 12:29:55 阅读更多 →

最新新闻

Spark Streaming vs Flink:流处理选型核心差异与实战决策指南

Spark Streaming vs Flink:流处理选型核心差异与实战决策指南

1. 流处理选型这件事,为什么值得花时间掰扯清楚做实时数据处理的人,迟早会撞上同一个岔路口:Spark Streaming 和 Flink 到底选哪个。这个问题在技术社区里被反复讨论,但大多数对比文章停留在"Flink 是纯流、Spark 是微批&quo…

2026/10/21 4:35:50 阅读更多 →
OpenClaw 2026.3.28 Windows 原生部署:角色初始化与排错全流程

OpenClaw 2026.3.28 Windows 原生部署:角色初始化与排错全流程

把OpenClaw 2026.3.28 跑进 Windows 原生环境,是我近期做得最折腾也最值的一件事。这里的“原生”,我指的是不套 WSL、不丢容器镜像,直接在 Windows 桌面系统的本地环境里装依赖、起服务、完成角色初始化并把角色跑起来。这篇文章把整个部署过…

2026/10/21 4:35:50 阅读更多 →
阿里EDAS示例工程上云实战:本地启动、部署配置与灰度避坑指南

阿里EDAS示例工程上云实战:本地启动、部署配置与灰度避坑指南

简介:这份资源是阿里EDAS(企业级分布式应用服务平台)的入门示例工程,面向希望学习微服务架构与云端应用部署的Java开发者,尤其适合刚接触EDAS、需要动手实践的中初级人员。压缩包共15个文件,以8个xml配置文…

2026/10/21 4:35:50 阅读更多 →
Redis哨兵实战:高可用架构部署、故障转移与踩坑经验

Redis哨兵实战:高可用架构部署、故障转移与踩坑经验

Redis 哨兵这套东西,我最早接触是在一个电商促销活动前夜。当时主从架构跑得好好的,结果主节点凌晨挂了,运维手动切了四十分钟,老板在群里连发十几条消息。从那以后我就把哨兵当成了高可用方案的标配。这篇文章不讲教科书定义&…

2026/10/21 4:35:50 阅读更多 →
C# WinForm Panel 边框颜色改不动?三种自绘方案一次讲清

C# WinForm Panel 边框颜色改不动?三种自绘方案一次讲清

简介:面向C# WinForm开发者的Panel控件美化方案,解决默认边框颜色与宽度不可直接调整的问题。资源通过继承Panel并重写OnPaint方法,以Graphics绘制自定义边框,提供BorderColor与BorderWidth两个扩展属性,示例代码清晰易…

2026/10/21 4:35:50 阅读更多 →
BMI160六轴传感器驱动开发实战:从寄存器配置到姿态解算的完整指南

BMI160六轴传感器驱动开发实战:从寄存器配置到姿态解算的完整指南

简介:面向STM32F030平台的博世BMI160自写驱动代码,通过I2C总线实现三轴陀螺仪的基础数据交互,适用于嵌入式开发者学习传感器驱动编写或快速搭建原型。压缩包共3个文件、仅6KB,包含BMI160.c源文件以及BMI160.h、BMI160_prm.h两个头…

2026/10/21 4:34:49 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →