Rust异步编程实战指南
作者:liuxs1998
Rust异步编程主要靠async/await 语法和异步运行时来写非阻塞代码,核心是把任务表示成 Future,再由运行时(如 Tokio)去驱动执行,它适合高并发 I/O 场景,比如网络服务、文件读写、消息处理等。
#[tokio::main]
async fn main() -> Result<()> {
// Open a connection to the mini-redis address.
let mut client = client::connect("127.0.0.1:6379").await?;
// Set the key "hello" with value "world"
client.set("hello", "world".into()).await?;
// Get key "hello"
let result = client.get("hello").await?;
println!("got value from the server; result={:?}", result);
Ok(())
}这段代码是 Tokio 官方教程 mini-redis 的客户端示例,演示了用 async/await 连接 Redis 服务、设置和读取键值对。
📜 逐行解析
#[tokio::main]
async fn main() -> Result<()> {#[tokio::main]:这是属性宏,把 async fn main 转换成 Tokio 运行时的入口。展开后相当于:
fn main() {
tokio::runtime::Runtime::new().unwrap().block_on(async {
// 你的 async main 代码
});
}它负责创建 Tokio 运行时、启动执行器、阻塞等待 main Future 完成。
-> Result<()>:main 返回 Result,这样可以用 ? 传播错误。这里的 Result 通常是 anyhow::Result 或 std::io::Result。如果返回 Err,程序会以非零状态码退出并打印错误。
let mut client = client::connect("127.0.0.1:6379").await?;
client::connect(...):异步连接 Redis 服务器。.await表示挂起当前任务,直到连接建立完成。?:如果连接失败,直接返回Err,不会继续执行。mut:因为后面要调用client.set(...)和client.get(...),这些方法需要&mut self。
client.set("hello", "world".into()).await?;
set:发送SET hello world命令。"world".into():转换成Bytes类型(mini-redis 用的字节缓冲区)。.await?:等待命令执行完成,失败则传播错误。
let result = client.get("hello").await?;
println!("got value from the server; result={:?}", result);
get:发送GET hello命令,返回Option<Bytes>。{:?}:用Debug格式打印,因为Bytes不一定实现了Display。- 如果 key 存在,
result是Some(Bytes);否则是None。
🔍 背后的运行时机制
这段代码能跑起来,靠的是 Tokio 运行时在背后做这些事:
#[tokio::main]创建运行时,把main作为第一个 Future 交给执行器。client::connect(...).await发起 TCP 连接。连接没建立时返回Pending,执行器去跑其他任务。连接建立后,epoll 事件唤醒任务,继续执行。client.set(...).await发送请求并等待响应。等待期间任务挂起,不阻塞线程。client.get(...).await同理。
整个过程中,当前线程没有被阻塞。如果同时有多个客户端任务,它们会在同一个线程上协作式交替执行。
🆚 对比:同步版本长什么样
如果用同步阻塞的方式写,代码会是这样:
fn main() -> Result<()> {
let mut client = std::net::TcpStream::connect("127.0.0.1:6379")?;
client.write_all(b"SET hello world\r\n")?;
let mut buf = [0u8; 1024];
let n = client.read(&mut buf)?;
println!("got value: {:?}", &buf[..n]);
Ok(())
}区别在于:同步版本在 connect、write、read 时线程被挂起,什么都干不了。异步版本在等待时让出 CPU,执行器可以跑其他任务。
📌 一句话总结
这段代码用 #[tokio::main] 启动 Tokio 运行时,通过 .await 异步地连接 Redis、设置和读取键值。每个 .await 点都是任务让出 CPU 的时机,等待期间线程不阻塞,执行器可以调度其他任务。这就是异步非阻塞的典型写法。
到此这篇关于Rust异步编程实战指南的文章就介绍到这了,更多相关rust异步编程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
