Rust Async 并发编程:任务、消息传递与 `join`

1. 创建异步任务

在传统的多线程模型中,我们使用 std::thread::spawn 来创建新的线程。而在 async 模型中,使用 spawn_task 代替 thread::spawn 来创建异步任务,并结合 await 关键字来处理异步操作。

示例:使用 spawn_task 进行并发计算

rust 复制代码
use trpl::{spawn_task, sleep};
use std::time::Duration;

#[tokio::main]
async fn main() {
    let handle = spawn_task(async {
        for i in 1..=5 {
            println!("hi number {} from the second task!", i);
            sleep(Duration::from_millis(500)).await;
        }
    });

    for i in 1..=5 {
        println!("hi number {} from the first task!", i);
        sleep(Duration::from_millis(500)).await;
    }

    handle.await.unwrap();
}

运行结果(实际输出顺序可能不同):

text 复制代码
hi number 1 from the second task!
hi number 1 from the first task!
hi number 2 from the first task!
hi number 2 from the second task!
...

在该示例中,我们创建了一个新的异步任务 spawn_task,并在主任务中执行另一个循环。因为 sleepasync 版本的,所以 await 允许其他任务在 sleep 期间继续执行,而不会阻塞整个线程。

2. 使用 join 让多个任务同时运行

当我们想要同时运行多个 async 任务并等待它们全部完成时,可以使用 trpl::join。它类似于 std::thread::JoinHandle::join(),但适用于 async 任务。

示例:使用 trpl::join 运行两个异步任务

rust 复制代码
use trpl::{join, sleep};
use std::time::Duration;

#[tokio::main]
async fn main() {
    let fut1 = async {
        for i in 1..=5 {
            println!("Task 1: {}", i);
            sleep(Duration::from_millis(500)).await;
        }
    };

    let fut2 = async {
        for i in 1..=5 {
            println!("Task 2: {}", i);
            sleep(Duration::from_millis(500)).await;
        }
    };

    join!(fut1, fut2).await;
}

输出顺序保持一致

text 复制代码
Task 1: 1
Task 2: 1
Task 1: 2
Task 2: 2
...

相比于 spawn_tasktrpl::join 提供了更加确定的执行顺序,因为 join! 会公平地轮询任务,使得它们不会出现一个任务远远领先于另一个的情况。

3. 通过消息通道在任务间传递数据

std::mpsc::channel 类似,Rust 也提供了 trpl::channel 来进行异步消息传递。这种方式避免了共享内存带来的数据竞争问题。

示例:异步消息传递

rust 复制代码
use trpl::{channel, sleep};
use std::time::Duration;

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

    let sender = async move {
        let messages = vec!["Hello", "from", "the", "async", "world!"];
        for msg in messages {
            tx.send(msg).await.unwrap();
            sleep(Duration::from_millis(500)).await;
        }
    };

    let receiver = async move {
        while let Some(msg) = rx.recv().await {
            println!("Received: {}", msg);
        }
    };

    trpl::join(sender, receiver).await;
}

输出

text 复制代码
Received: Hello
Received: from
Received: the
Received: async
Received: world!

任务自动关闭

上面的代码能正确终止,因为 txasync move 块结束时会被 drop,从而让 rx.recv() 返回 None,结束 while let 循环。

如果我们希望多个任务发送数据,只需 clone 发送端:

rust 复制代码
let tx1 = tx.clone();
let sender1 = async move {
    tx1.send("Extra message").await.unwrap();
};

使用 trpl::join3 让多个任务并发执行。

4. 总结

  • spawn_task 用于创建新的异步任务。
  • await 允许任务在等待时释放 CPU 资源,提高并发性能。
  • trpl::join 确保多个任务公平执行。
  • trpl::channel 实现了异步消息传递,避免了数据竞争。
  • async move 确保所有权正确传递,避免任务执行过程中 tx 被持有过久导致 rx.recv() 无限等待。

Rust 的异步编程提供了强大的并发能力,同时通过 await 避免了传统多线程编程的阻塞问题,使得代码更高效、更易读。希望本文能帮助你更好地理解 async 并发编程的核心概念!

相关推荐
aramae6 分钟前
MySQL复合查询(8)
java·c语言·开发语言·后端·算法
Rain的Java大神之路39 分钟前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
荆棘鸟智能43 分钟前
城市感知设备怎么统一接入?从多协议网关到设备模型的中间件架构设计
人工智能·算法·边缘计算
Wang's Blog1 小时前
Java 接入Redis: 通用命令与键管理
java·服务器·redis
Wang's Blog1 小时前
Java 接入Redis: 列表集合与有序集合操作命令
java·服务器·redis
Ivanqhz2 小时前
SVD++算法
java·服务器·网络·深度学习·神经网络
qq_2518364573 小时前
springboot vue3 开发实现 拼豆管理系统
java·开发语言·ai编程
caoerzhong3 小时前
JeeWMS 开源仓库管理系统全景解读:一套 Java WMS 如何把 WMS/OMS/BMS/TMS 装进同一个系统
java·开源
曹牧3 小时前
Java集合框架:List、ArrayList、Map 和 HashMap
java
顾城猿3 小时前
STOMP 协议
java