展开描述
用于编写可靠的网络应用程序而不影响速度的运行时。
Tokio 是一个事件驱动、非阻塞的 I/O 平台,用于使用 Rust 编程语言编写异步应用程序。从高层次上讲,它提供了几个主要组件:
- 用于使用异步任务的工具,包括同步原语和通道以及超时、休眠和 定时器。
- 用于执行异步 I/O的 API,包括 TCP 和 UDP 套接字、 文件系统操作,以及 进程和 信号管理。
- 用于执行异步代码的运行时,包括任务调度器、由操作系统事件队列(
epoll、kqueue、IOCP等)支持的 I/O 驱动,以及一个高性能定时器。
指南级文档可在官网上找到。
§Tokio 概览
Tokio 由许多模块组成,这些模块提供了在 Rust 中实现异步应用程序所需的各种功能。在本节中,我们将简要地浏览 Tokio,概括其主要 API 及其用途。
最简单的上手方式是启用所有特性。请通过启用 full 特性标志来实现:
tokio = { version = "1", features = ["full"] }§编写应用程序
Tokio 非常适合编写应用程序,对于大多数用户来说,不需要过于担心应该选择哪些特性。如果你不确定,我们建议使用 full,以确保在构建应用程序时不会遇到任何阻碍。
§示例
本示例展示使用 Tokio 最快速的上手方法。
tokio = { version = "1", features = ["full"] }§编写库
作为库的作者,你的目标应该是提供基于 Tokio 的最轻量级 crate。为此,你应该确保只启用你需要的特性。这样用户就可以使用你的 crate,而无需启用不必要的特性。
§示例
本示例展示对于一个只需要 tokio::spawn 并使用 TcpStream 的库,应当如何引入特性。
tokio = { version = "1", features = ["rt", "net"] }§使用任务
Rust 中的异步程序围绕轻量级、非阻塞的执行单元(即任务)构建。tokio::task 模块提供了处理任务的重要工具:
spawn函数和JoinHandle类型,分别用于在 Tokio 运行时上调度一个新任务 以及等待已生成任务的输出,- 用于在异步任务上下文中运行阻塞操作的函数。
tokio::task 模块仅在启用了 “rt” 特性标志时可用。
tokio::sync 模块包含在需要通信或共享数据时使用的同步原语。其中包括:
- 通道(
oneshot、mpsc、watch和broadcast),用于在任务之间 发送值, - 一个非阻塞的
Mutex,用于控制对共享可变值的访问, - 一个异步的
Barrier类型,用于在开始计算前让多个任务同步。
tokio::sync 模块仅在启用了 “sync” 特性标志时可用。
tokio::time 模块提供了跟踪时间和调度工作的工具。这包括为任务设置超时、
将工作休眠到将来某个时间再运行,或以固定间隔重复执行某个操作的函数。
要使用 tokio::time,必须启用 “time” 特性标志。
最后,Tokio 提供了一个用于执行异步任务的运行时。大多数应用程序可以使用 #[tokio::main] 宏来在 Tokio 运行时上运行它们的代码。但是,该宏只提供基本的配置选项。作为替代方案,tokio::runtime 模块提供了更强大的 API 来配置和管理运行时。如果 #[tokio::main] 宏不能满足你的需求,应当使用该模块。
使用运行时需要 “rt” 或 “rt-multi-thread” 特性标志,分别用于启用当前线程的单线程调度器和多线程
调度器。详情请参阅runtime 模块文档。此外,“macros” 特性
标志会启用 #[tokio::main] 和 #[tokio::test] 属性。
§CPU 密集型任务与阻塞代码
Tokio 能够通过在线程上反复切换当前正在运行的任务,在少量线程上并发运行大量任务。但是,这种切换只能发生在 .await 处,因此长时间无法到达 .await 的代码会阻止其他任务运行。为了解决这一问题,Tokio 提供了两种线程:核心线程和阻塞线程。
核心线程是所有异步代码运行的地方,Tokio 默认会为每个 CPU 核心派生一个核心线程。你可以使用环境变量 TOKIO_WORKER_THREADS 来覆盖默认值。
阻塞线程按需派生,可用于运行那些否则会阻塞其他任务运行的
阻塞代码。它们在一段时间内未被使用时会被保留,该时间可以通过 thread_keep_alive
进行配置。由于 Tokio 不可能像对异步代码那样换出阻塞任务,阻塞线程数量的上限非常大。这些限制可以在 Builder 上配置。
要派生一个阻塞任务,应当使用 spawn_blocking 函数。
#[tokio::main]
async fn main() {
// 这段代码运行在核心线程上。
let blocking_task = tokio::task::spawn_blocking(|| {
// 这段代码运行在阻塞线程上。
// 在这里阻塞是允许的。
});
// 我们可以像这样等待阻塞任务完成:
// 如果阻塞任务 panic,下面的 unwrap 将传播该 panic。
blocking_task.await.unwrap();
}如果你的代码是 CPU 密集型的,并且希望限制用于运行它的线程数量,则应使用专用于 CPU 密集型任务的单独线程池。例如,可以考虑使用 rayon 库来处理 CPU 密集型任务。也可以创建一个专用于 CPU 密集型任务的额外 Tokio 运行时,但如果这样做,需要小心,让该额外运行时仅运行 CPU 密集型任务,因为在该运行时上运行 I/O 密集型任务时表现会很差。
提示:如果使用 rayon,可以在 rayon 任务完成时使用 oneshot 通道将结果发送回 Tokio。
§异步 I/O
除了调度和运行任务外,Tokio 还提供了异步执行输入和输出所需的一切功能。
tokio::io 模块提供了 Tokio 的异步核心 I/O 原语,即 AsyncRead、AsyncWrite 和 AsyncBufRead 这些 trait。此外,当启用了 “io-util” 特性标志时,它还提供了用于处理这些 trait 的组合子和函数,作为 std::io 的异步对应物。
Tokio 还包含用于执行各种 I/O 以及与操作系统异步交互的 API。其中包括:
tokio::net,包含 TCP、UDP 和 Unix 域套接字的非阻塞版本(通过 “net” 特性标志启用),tokio::fs,类似于std::fs,但用于异步执行文件系统 I/O (通过 “fs” 特性标志启用),tokio::signal,用于异步处理 Unix 和 Windows 操作系统的信号 (通过 “signal” 特性标志启用),tokio::process,用于派生和管理子进程(通过 “process” 特性标志启用)。
§示例
一个简单的 TCP 回显服务器:
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = [0; 1024];
// 在循环中,从套接字读取数据并将其写回。
loop {
let n = match socket.read(&mut buf).await {
// 套接字已关闭
Ok(0) => return,
Ok(n) => n,
Err(e) => {
eprintln!("failed to read from socket; err = {:?}", e);
return;
}
};
// 将数据写回
if let Err(e) = socket.write_all(&buf[0..n]).await {
eprintln!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
}§特性标志
Tokio 使用一组特性标志来减少编译代码量。可以只启用其中某些特性而不启用其他特性。默认情况下,Tokio 不会启用任何特性,但允许你为用例启用一个子集。下面是可用特性标志的列表。你可能还会注意到在每个函数、结构体和 trait 上方会列出使用该项所需的一个或多个特性标志。如果你是 Tokio 的新手,建议使用 full 特性标志,它会启用所有公开 API。但请注意,这会引入许多你可能不需要的额外依赖。
full:启用下面列出的所有特性,test-util和不稳定特性除外。rt:启用tokio::spawn、当前线程调度器 以及非调度器相关的工具。rt-multi-thread:启用较重的、多线程的工作窃取调度器。io-util:启用基于 I/O 的Exttrait。io-std:启用Stdout、Stdin和Stderr类型。net:启用tokio::net中的类型,如TcpStream、UnixStream和UdpSocket,以及(类 Unix 系统上的)AsyncFd和(FreeBSD 上的)PollAio。time:启用tokio::time中的类型,并允许调度器启用 内置定时器。process:启用tokio::process中的类型。macros:启用#[tokio::main]和#[tokio::test]宏。sync:启用所有tokio::sync类型。signal:启用所有tokio::signal类型。fs:启用tokio::fs类型。test-util:启用 Tokio 运行时的测试相关基础设施。parking_lot:作为一种潜在优化,内部使用 [parking_lot] crate 的 同步原语。此外,该依赖也是在const上下文中构造 我们某些原语所必需的。MSRV可能会根据所使用 的 [parking_lot] 版本而提高。
注意:AsyncRead 和 AsyncWrite trait 不需要任何特性,始终可用。
§不稳定特性
某些特性标志仅在指定了 tokio_unstable 标志时才可用:
tracing:启用 tracing 事件。io-uring:启用io-uring(仅限 Linux)。taskdump:启用taskdump(仅限 Linux)。
同样,此标志可以访问不稳定的 API。
此标志启用不稳定特性。这些特性的公开 API 可能会在 1.x 版本中发生破坏性变更。要启用这些特性,在编译时必须向 rustc 传递 --cfg tokio_unstable 参数。这样做是为了显式选择加入可能违反 semver 约定的特性,因为 Cargo 尚不直接支持此类选择加入。
可以在项目的 .cargo/config.toml 文件中指定它:
[build]
rustflags = ["--cfg", "tokio_unstable"][build] 节不是放在 Cargo.toml 文件中,
而必须放在 Cargo 配置文件 .cargo/config.toml 中。
或者,可以通过环境变量来指定它:
## Many *nix shells:
export RUSTFLAGS="--cfg tokio_unstable"
cargo build## Windows PowerShell:
$Env:RUSTFLAGS="--cfg tokio_unstable"
cargo build§支持的平台
Tokio 目前保证支持以下平台:
- Linux
- Windows
- Android(API 级别 21)
- macOS
- iOS
- FreeBSD
Tokio 将在未来继续支持这些平台。但是,未来的版本可能会更改一些要求,例如 Linux 上所需的最低 libc 版本、Android 上的 API 级别,或受支持的 FreeBSD 版本。
除上述平台外,Tokio 旨在能在 mio crate 支持的所有平台上运行。可以在 mio 的文档中找到更长的平台列表。但是,这些额外的平台在未来可能会变得不再受支持。
请注意,Wine 被视为与 Windows 不同的平台。有关 Wine 支持的更多信息,请参阅 mio 的文档。
§WASM support
Tokio 对 WASM 平台有一些有限的支持。在不使用 tokio_unstable 标志的情况下,支持以下特性:
syncmacrosio-utilrttime
启用任何其他特性(包括 full)都会导致编译失败。
time 模块仅在支持定时器的 WASM 平台(例如 wasm32-wasi)上可用。在不支持定时器的 WASM 平台上使用时,相关的时间函数会 panic。
还需注意,如果运行时无限期地空闲,它会立即 panic,而不是永远阻塞。在不支持时间的平台上,这意味着运行时永远不能以任何方式处于空闲状态。
§Unstable WASM support
Tokio 还不稳定地支持一些额外的 WASM 特性。这需要使用 tokio_unstable 标志。
使用此标志可以在 wasm32-wasi 目标上使用 tokio::net。但是,并非所有方法都可在网络类型上使用,因为 WASI 目前不支持从 WASM 内部创建新套接字。因此,目前必须通过 FromRawFd trait 来创建套接字。
重新导出§
pub use task::spawn;
模块§
- io
- 异步 I/O 功能的 trait、辅助类型和类型定义。
- net
tokio的 TCP/UDP/Unix 绑定。- runtime
- Tokio 运行时。
- stream
- 由于
Streamtrait 进入std的时间晚于 Tokio 1.0 发布, Tokio 的大多数流相关工具已迁移到tokio-streamcrate 中。 - sync
- 用于异步上下文的同步原语。
- task
- 异步的绿色线程。
- time
- 用于跟踪时间的工具。
宏§
- pin
- 在栈上固定一个值。
- task_
local - 声明一个类型为
tokio::task::LocalKey的新任务局部键。