Skip to content
当前页大纲

Rust 异步编程 async/await

rust
在现代编程中,异步编程变得越来越重要,因为它允许程序在等待 I/O 操作(如文件读写、网络通信等)时不被阻塞,从而提高性能和响应性。

异步编程是一种在 Rust 中处理非阻塞操作的方式,允许程序在执行长时间的 I/O 操作时不被阻塞,而是在等待的同时可以执行其他任务。

Rust 提供了多种工具和库来实现异步编程,包括 asyncawait 关键字、futures 和异步运行时(如 tokio、async-std 等),以及其他辅助工具。

FutureFutureRust 中表示异步操作的抽象。它是一个可能还没有完成的计算,将来某个时刻会返回一个值或一个错误。

async/awaitasync 关键字用于定义一个异步函数,它返回一个 Futureawait 关键字用于暂停当前 Future 的执行,直到它完成。

实例
以下实例展示了如何使用 asyncawait 关键字编写一个异步函数,以及如何在异步函数中执行异步任务并等待其完成。

实例
// 引入所需的依赖库
use tokio;

// 异步函数,模拟异步任务
async fn async_task() -> u32 {
    // 模拟异步操作,等待 1 秒钟
    tokio::time::delay_for(std::time::Duration::from_secs(1)).await;
    // 返回结果
    42
}

// 异步任务执行函数
async fn execute_async_task() {
    // 调用异步任务,并等待其完成
    let result = async_task().await;
    // 输出结果
    println!("Async task result: {}", result);
}

// 主函数
#[tokio::main]
async fn main() {
    println!("Start executing async task...");
    // 调用异步任务执行函数,并等待其完成
    execute_async_task().await;
    println!("Async task completed!");
}
以上代码中,我们首先定义了一个异步函数 async_task(),该函数模拟了一个异步操作,使用 tokio::time::delay_for() 方法来等待 1 秒钟,然后返回结果 42。接着定义了一个异步任务执行函数 execute_async_task(),在其中调用了异步函数,并使用 await 关键字等待异步任务的完成。最后在 main 函数中使用 tokio::main 宏来运行异步任务执行函数,并等待其完成。

运行该程序,可以看到程序输出了开始执行异步任务的提示,然后等待了 1 秒钟后输出了异步任务的结果,并最终输出了异步任务完成的提示:

Start executing async task...
Async task result: 42
Async task completed!
这个例子演示了 Rust 中使用 asyncawait 关键字编写异步函数,以及如何在异步函数中执行异步任务并等待其完成。

以下实例使用 tokio 库执行异步 HTTP 请求,并输出响应结果:

实例 2
// 引入所需的依赖库
use std::error::Error;
use tokio::runtime::Runtime;
use reqwest::get;

// 异步函数,用于执行 HTTP GET 请求并返回响应结果
async fn fetch_url(url: &str) -> Result<String, Box<dyn Error>> {
    // 使用 reqwest 发起异步 HTTP GET 请求
    let response = get(url).await?;
    let body = response.text().await?;
    Ok(body)
}

// 异步任务执行函数
async fn execute_async_task() -> Result<(), Box<dyn Error>> {
    // 发起异步 HTTP 请求
    let url = "https://jsonplaceholder.typicode.com/posts/1";
    let result = fetch_url(url).await?;
    // 输出响应结果
    println!("Response: {}", result);
    Ok(())
}

// 主函数
fn main() {
    // 创建异步运行时
    let rt = Runtime::new().unwrap();
    // 在异步运行时中执行异步任务
    let result = rt.block_on(execute_async_task());
    // 处理异步任务执行结果
    match result {
        Ok(_) => println!("Async task executed successfully!"),
        Err(e) => eprintln!("Error: {}", e),
    }
}
以上代码中,我们首先引入了 tokio 和 reqwest 库,分别用于执行异步任务和进行 HTTP 请求。然后定义了一个异步函数 fetch_url,用于执行异步的 HTTP GET 请求,并返回响应结果。

接着定义了一个异步任务执行函数 execute_async_task,该函数在其中发起了异步 HTTP 请求,并输出响应结果。

最后,在 main 函数中创建了一个 tokio 异步运行时,并在其中执行了异步任务,处理了异步任务的执行结果。

运行该程序,可以看到输出了异步 HTTP 请求的响应结果,实例中请求了 JSONPlaceholder 的一个帖子数据,并打印了其内容。

异步编程说明

rust
async 关键字
async 关键字用于定义异步函数,即返回 Futureimpl Future 类型的函数。异步函数执行时会返回一个未完成的 Future 对象,它表示一个尚未完成的计算或操作。

异步函数可以包含 await 表达式,用于等待其他异步操作的完成。

实例
async fn hello() -> String {
    "Hello, world!".to_string()
}

await 关键字

rust
await 关键字用于等待异步操作的完成,并获取其结果。

await 表达式只能在异步函数或异步块中使用,它会暂停当前的异步函数执行,等待被等待的 Future 完成,然后继续执行后续的代码。

实例
async fn print_hello() {
    let result = hello().await;
    println!("{}", result);
}

异步函数返回值

rust
异步函数的返回值类型通常是 impl Future<Output = T>,其中 T 是异步操作的结果类型。由于异步函数的返回值是一个 Future,因此可以使用 .await 来等待异步操作的完成,并获取其结果。

实例
async fn add(a: i32, b: i32) -> i32 {
    a + b
}

异步块

rust
除了定义异步函数外,Rust 还提供了异步块的语法,可以在同步代码中使用异步操作。异步块由 async { } 构成,其中可以包含异步函数调用和 await 表达式。

实例
async {
    let result1 = hello().await;
    let result2 = add(1, 2).await;
    println!("Result: {}, {}", result1, result2);
};

异步任务执行

rust
Rust 中,异步任务通常需要在执行上下文中运行,可以使用 tokio::main、async-std 的 task::block_on 或 futures::executor::block_on 等函数来执行异步任务。这些函数会接受一个异步函数或异步块,并在当前线程或执行环境中执行它。

实例
use async_std::task;

fn main() {
    task::block_on(print_hello());
}

错误处理

rust
await 后面跟一个 ? 操作符可以传播错误。如果 awaitFuture 完成时返回了一个错误,那么这个错误会被传播到调用者。

实例
async fn my_async_function() -> Result<(), MyError> {
    some_async_operation().await?;
    // 如果 some_async_operation 出错,错误会被传播
}

异步 trait 方法

rust
Rust 允许为 trait 定义异步方法。这使得你可以为不同类型的对象定义异步操作。

实例
trait MyAsyncTrait {
    async fn async_method(&self) -> Result<(), MyError>;
}

impl MyAsyncTrait for MyType {
    async fn async_method(&self) -> Result<(), MyError> {
        // 异步逻辑
    }
}

异步上下文

rust
Rust 中,异步代码通常在异步运行时(如 Tokioasync-std)中执行。这些运行时提供了调度和执行异步任务的机制。

实例
#[tokio::main]
async fn main() {
    some_async_operation().await;
}
以上代码中,#[tokio::main] 属性宏将 main 函数包装在一个异步运行时中。

异步宏

rust
Rust 提供了一些异步宏,如 tokio::spawn,用于在异步运行时中启动新的异步任务。

实例
#[tokio::main]
async fn main() {
    let handle = tokio::spawn(async {
        // 异步逻辑
    });
    handle.await.unwrap();
}

异步 I/O

rust
Rust 的标准库提供了异步 I/O 操作,如 tokio::fs::Fileasync_std::fs::File

实例
use tokio::fs::File;
use tokio::io::{self, AsyncReadExt};

#[tokio::main]
async fn main() -> io::Result<()> {
    let mut file = File::open("file.txt").await?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).await?;
    println!("Contents: {}", contents);
    Ok(())
}

异步通道

rust
Rust 的一些异步运行时提供了异步通道(如 tokio::sync::mpsc),允许在异步任务之间传递消息。

实例
use tokio::sync::mpsc;
use tokio::spawn;

#[tokio::main]
async fn main() {
    let (tx, mut rx) = mpsc::channel(32);

    let child = spawn(async move {
        let response = "Hello, world!".to_string();
        tx.send(response).await.unwrap();
    });

    let response = rx.recv().await.unwrap();
    println!("Received: {}", response);

    child.await.unwrap();
}

总结

rust
Rust 的异步编程模型 async/await 提供了一种简洁、高效的方式来处理异步操作。

它允许开发者以一种更自然和直观的方式来处理异步操作,同时保持了 Rust 的安全性和性能。

通过 async/awaitRust 为异步编程提供了一流的语言支持,使得编写高效且可读性强的异步程序变得更加容易。

MIT License.