深入 Rust 异步生态:探索运行时、执行器与社区库
Rust 是一门以性能和安全性为核心的编程语言,近年来,在处理异步编程方面也表现出色。尽管 Rust 标准库为编写异步代码提供了基础要素,但为了构建完整的异步应用程序,还需要依赖社区提供的异步生态系统。本章将介绍 Rust 异步生态系统的关键组成部分,包括异步运行时、执行器、以及它们与 I/O 操作的兼容性,帮助开发者理解如何选择适合的工具来支持异步编程。
Rust 提供了基础的异步支持,但缺乏用于执行异步任务的运行时、执行器以及其他关键组件,这些需求由社区的异步库(crates)填补。异步运行时是执行异步代码的核心,它们通常结合反应器和执行器来处理异步 I/O、计时器和进程间通信等外部事件。常见的异步运行时包括 Tokio、Async-std 和 Smol,每个都有不同的特点和用途。在选择异步框架时,开发者需要考虑运行时的线程模型(单线程 vs 多线程)、性能需求以及与其他库的兼容性。为了确保代码能够高效执行,开发者还应关注任务的调度和线程之间的数据同步问题。
The Async Ecosystem
Rust 没提供什么
- Rust 目前只提供编写 async 代码的基本要素,标准库中尚未提供执行器、任务、反应器、组合器以及低级 I/O future 和 trait
- 社区提供的 async 生态系统填补了这些空白
Async 运行时
- Async 运行时是用于执行 async 应用程序的库
- 运行时通常将一个反应器与一个或多个执行器捆绑在一起
- 反应器为异步I/O、进程间通信和计时器等外部事件提供订阅机制
- 在 async 运行时中,订阅者通常是代表低级别 I/O 操作的 future
- 执行者负责安排和执行任务
- 它们跟踪正在运行和暂停的任务,对future进行 poll 直到完成,并在任务能够取得进展时唤醒任务
- “执行者”一词经常与“运行时”互换使用
- 我们使用“生态系统”一词来描述一个与兼容 trait 和特性捆绑在一起的运行时
社区提供的 async crates
- futures crate ,提供了 Stream、Sink、AsyncRead、AsyncWrite 等 trait,以及组合器等工具。这些可能最终会成为标准库的一部分
- Futures 有自己的执行器,但没有自己的反应器,因此它不支持 async I/O 或计时器 future 的执行
- 因此,它不被认为是完整的运行时
- 常见的选择是: 与另一个 crate 中的执行器一起使用来自 futures 提供的工具
流行的运行时
- Tokio:一个流行的 async 生态系统,包含 HTTP、gRPC 和跟踪框架
- Async-std:提供标准库的 async 副本
- Smol:小型、简化的 async 运行时。提供可用于包装 UnixStream 或 TcpListener 等结构的 async trait
- Fuchsia-async:用于 Fuchsia OS 的执行器
确定生态兼容性
- 与 async I/O、计时器、进程间通信或任务交互的 async 代码通常取决于特定的异步执行器或反应器
- 所有其他 async 代码,如异步表达式、组合器、同步类型和流,通常与生态系统无关,前提是任何嵌套的 future 也与生态系统无关
- 在开始一个项目之前,建议研究相关的 async 框架和库,以确保与您选择的运行时以及彼此之间的兼容性
单线程 VS 多线程执行器
- async 执行器可以是单线程或多线程的
- 多线程执行器可以在多个任务上同时取得进展,对于有许多任务的工作负载,它可以大大加快执行速度,但在任务之间同步数据通常更昂贵
- 在单线程运行时和多线程运行时之间进行选择时,建议测量应用程序的性能
- 任务可以在创建它们的线程上运行,也可以在单独的线程上运行
- 异步运行时通常提供将任务生成到单独线程的功能
- 即使任务在不同的线程上执行,它们也应该是非阻塞的
- 为了在多线程执行器上调度任务,它们必须是 Send 的
- 一些运行时提供了生成非 Send 的任务的函数,确保每个任务都在生成它的线程上执行
- 它们还可以提供将阻塞任务生成到专用线程的函数,这对于运行来自其他库的阻塞同步代码非常有用
总结
Rust 的异步编程生态系统在社区的共同努力下已经成熟,尽管它起步较晚。理解异步运行时和执行器的角色,以及如何选择合适的框架,至关重要,尤其是在开发高效的异步应用时。开发者应关注生态兼容性,特别是与异步 I/O、计时器和进程间通信的交互。同时,合理选择单线程或多线程执行器,不仅能提升应用性能,还能确保任务调度和数据同步的高效性。