Rust 异步编程高级应用指南
1. 什么是异步编程?
异步编程是一种编程范式,允许程序在等待 I/O 操作完成时继续执行其他任务,而不是阻塞线程。在 Rust 中,异步编程主要通过 async/await 语法和 tokio 等运行时库来实现。
async fn hello() {
println!("Hello");
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
println!("World");
}
fn main() {
tokio::runtime::Runtime::new().unwrap().block_on(hello());
}
2. Future 特质
Future 是 Rust 中表示异步操作结果的特质,它定义了如何将异步操作转换为最终结果。
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
struct MyFuture {
count: u32,
}
impl Future for MyFuture {
type Output = u32;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.count >= 5 {
Poll::Ready(self.count)
} else {
// 告诉 executor 我们还没准备好
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
3. 任务管理
3.1 生成任务
使用 tokio::spawn 可以生成一个新的异步任务,它会在后台执行。
async fn task1() -> String {
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
"Task 1 done".to_string()
}
async fn task2() -> String {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
"Task 2 done".to_string()
}
async fn main() {
let handle1 = tokio::spawn(task1());
let handle2 = tokio::spawn(task2());
let result1 = handle1.await.unwrap();
let result2 = handle2.await.unwrap();
println!("{result1}, {result2}");
}
3.2 任务取消
使用 AbortHandle 可以取消正在执行的任务。
use tokio::sync::oneshot;
async fn long_running_task() {
for i in 0..10 {
println!("Working... {i}");
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
}
async fn main() {
let (tx, rx) = oneshot::channel();
let handle = tokio::spawn(async move {
tokio::select! {
_ = long_running_task() => {
println!("Task completed");
}
_ = rx => {
println!("Task cancelled");
}
}
});
// 等待一段时间后取消任务
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
tx.send(()).unwrap();
handle.await.unwrap();
}
4. 流 (Stream)
Stream 是 Rust 中表示异步序列的特质,类似于迭代器,但用于异步场景。
use tokio::stream::StreamExt;
async fn main() {
let mut stream = tokio::stream::iter(vec![1, 2, 3, 4, 5]);
while let Some(item) = stream.next().await {
println!("Item: {item}");
}
}
4.1 自定义流
use tokio::stream::{Stream, StreamExt};
use std::pin::Pin;
use std::task::{Context, Poll};
struct Counter {
count: u32,
max: u32,
}
impl Stream for Counter {
type Item = u32;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let self_mut = self.get_mut();
if self_mut.count < self_mut.max {
let value = self_mut.count;
self_mut.count += 1;
Poll::Ready(Some(value))
} else {
Poll::Ready(None)
}
}
}
async fn main() {
let mut counter = Counter { count: 0, max: 5 };
while let Some(item) = counter.next().await {
println!("Count: {item}");
}
}
5. 选择器 (Selector)
使用 tokio::select! 宏可以等待多个异步操作中的任意一个完成。
async fn task1() -> String {
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
"Task 1 done".to_string()
}
async fn task2() -> String {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
"Task 2 done".to_string()
}
async fn main() {
let handle1 = tokio::spawn(task1());
let handle2 = tokio::spawn(task2());
tokio::select! {
result1 = handle1 => println!("Task 1 completed: {}", result1.unwrap()),
result2 = handle2 => println!("Task 2 completed: {}", result2.unwrap()),
}
}
6. 超时处理
使用 tokio::time::timeout 可以为异步操作设置超时。
async fn long_running_task() {
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
println!("Task completed");
}
async fn main() {
let result = tokio::time::timeout(
tokio::time::Duration::from_secs(2),
long_running_task()
).await;
match result {
Ok(_) => println!("Task completed within timeout"),
Err(_) => println!("Task timed out"),
}
}
7. 异步 I/O
7.1 异步文件操作
use tokio::fs;
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 读取文件
let content = fs::read_to_string("example.txt").await?;
println!("File content: {content}");
// 写入文件
fs::write("output.txt", "Hello, World!").await?;
println!("File written successfully");
Ok(())
}
7.2 异步网络操作
use tokio::net::{TcpListener, TcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
async fn handle_client(mut socket: TcpStream) {
let mut buffer = [0; 1024];
loop {
let n = match socket.read(&mut buffer).await {
Ok(n) if n == 0 => return,
Ok(n) => n,
Err(e) => {
eprintln!("Error reading from socket: {e}");
return;
}
};
if let Err(e) = socket.write_all(&buffer[0..n]).await {
eprintln!("Error writing to socket: {e}");
return;
}
}
}
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("Server listening on 127.0.0.1:8080");
loop {
let (socket, _) = listener.accept().await?;
tokio::spawn(handle_client(socket));
}
}
8. 实际应用场景
8.1 异步 HTTP 服务器
use warp::Filter;
async fn hello() -> Result<impl warp::Reply, warp::Rejection> {
Ok(warp::reply::json(&serde_json::json!({
"message": "Hello, World!"
})))
}
#[tokio::main]
async fn main() {
let routes = warp::path!("api" / "hello").and_then(hello);
println!("Server starting on http://localhost:3030");
warp::serve(routes)
.run(([127, 0, 0, 1], 3030))
.await;
}
8.2 异步数据库操作
use sqlx::postgres::PgPool;
async fn main() -> Result<(), sqlx::Error> {
let pool = PgPool::connect("postgres://username:password@localhost:5432/database").await?;
// 创建表
sqlx::query(r#"
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
)
"#).execute(&pool).await?;
// 插入数据
sqlx::query(r#"INSERT INTO users (name) VALUES ($1)"#)
.bind("Alice")
.execute(&pool)
.await?;
// 查询数据
let users = sqlx::query!("SELECT * FROM users")
.fetch_all(&pool)
.await?;
for user in users {
println!("User: {} - {}", user.id, user.name);
}
Ok(())
}
9. 最佳实践
- 使用
async/await语法:使用现代的async/await语法,而不是旧的Future组合器。 - 合理使用任务:对于需要并行执行的操作,使用
tokio::spawn创建任务。 - 使用
select!宏:对于需要等待多个异步操作的场景,使用select!宏。 - 处理超时:对于可能长时间运行的操作,使用
timeout设置超时。 - 错误处理:使用
Result类型和?运算符处理异步操作中的错误。 - 使用
Stream处理序列:对于异步序列,使用Stream特质。 - 避免阻塞:在异步代码中避免使用阻塞操作,如
std::thread::sleep。
10. 总结
Rust 的异步编程系统是一种强大的工具,特别适合 I/O 密集型任务。通过掌握其高级特性,我们可以编写更加高效、可维护的异步代码。
在实际应用中,我们可以使用异步编程构建高性能的网络应用、数据库操作和文件处理系统,充分发挥 Rust 的并发能力。
希望本文对你理解和应用 Rust 异步编程有所帮助!
640



被折叠的 条评论
为什么被折叠?



