Rust Async 并发编程:处理任意数量的 Future 与 Stream

1. Streams:异步数据流

1.1 Streams 与 Iterator 的异同

Rust 的 Iterator 是同步的,通过 next() 方法逐个获取数据。而 Stream 是 async 版本的 Iterator,它使用 next().await 来获取数据项。

示例:将 Iterator 转换为 Stream

rust 复制代码
use trpl::{stream_from_iter, StreamExt};

let numbers = vec![1, 2, 3, 4, 5];
let stream = stream_from_iter(numbers.into_iter());

while let Some(value) = stream.next().await {
    println!("Received: {}", value);
}

此示例中:

  • stream_from_iter() 将 Iterator 转换为 Stream。
  • 通过 stream.next().await 按顺序异步获取数据项。

2. 组合 Streams

2.1 构建 Stream 处理异步消息

在实际应用中,我们经常需要从网络、数据库或消息队列中接收数据。这时,可以用 trpl::channel 创建 Stream 来异步处理数据。

rust 复制代码
use trpl::{channel, ReceiverStream};

fn get_messages() -> impl Stream<Item = String> {
    let (tx, rx) = channel();

    spawn_task(async move {
        for letter in "abcdefghij".chars() {
            tx.send(letter.to_string()).await.unwrap();
        }
    });
    
    ReceiverStream::new(rx)
}

while let Some(msg) = get_messages().next().await {
    println!("Message: {}", msg);
}
  • get_messages 返回一个 Stream,每次 next().await 便能获取新的数据项。
  • 通过 spawn_task 启动异步任务,定期向 Stream 发送数据。

3. 控制 Stream 速率与超时

3.1 timeout:为 Stream 设置超时

当处理外部数据时,我们可能希望对每个 Stream 数据项设定超时时间,以避免某个数据源长时间无响应。

rust 复制代码
use trpl::{StreamExt, sleep, Duration};

let messages = get_messages().timeout(Duration::from_millis(200));

while let Some(result) = messages.next().await {
    match result {
        Ok(msg) => println!("Message: {}", msg),
        Err(_) => println!("Timeout occurred!"),
    }
}
  • timeout() 方法为 Stream 每个数据项设置超时时间。
  • 当数据在 200ms 内到达时,正常输出,否则触发超时逻辑。

3.2 throttle:限制 Stream 处理速率

有时,我们希望 Stream 以固定的速率生成数据,而不是尽可能快地处理。

rust 复制代码
use trpl::StreamExt;

let throttled_messages = get_messages().throttle(Duration::from_millis(100));
  • throttle() 方法限制 Stream 处理频率,每 100ms 处理一个数据项。
  • 避免 Stream 过快地填充下游处理逻辑。

4. 合并多个 Streams

4.1 merge:合并多个 Stream

在某些情况下,我们可能有多个 Stream 数据源,例如:

  • 一个 Stream 处理用户输入
  • 一个 Stream 处理传感器数据

可以使用 merge() 将它们合并到一个 Stream,以便统一处理:

rust 复制代码
let messages = get_messages().timeout(Duration::from_millis(200));
let intervals = get_intervals().map(|i| format!("Interval: {}", i));

let merged = messages.merge(intervals);

while let Some(event) = merged.next().await {
    println!("Received: {}", event);
}
  • messages 处理异步消息,带 200ms 超时。
  • intervals 生成时间间隔数据(Interval: 1, Interval: 2, ...)。
  • merge() 方法合并两个 Stream,同时接收消息和时间间隔。

4.2 take:限制 Stream 处理的项数

有时,我们希望 Stream 只处理有限数量的数据项。例如,限制为 10 条:

rust 复制代码
let limited_stream = merged.take(10);

这样,merged 只会输出 10 条数据,然后 Stream 自动结束。

5. 处理 Stream 可能的错误

在异步系统中,消息通道的 send 操作可能会失败,例如 tx.send(msg).await.unwrap();。

如果通道关闭,send 会返回 Err。因此,我们应当合理地处理这些错误,而不是 unwrap()。

rust 复制代码
if let Err(e) = tx.send(msg).await {
    println!("Error sending message: {:?}", e);
    break;
}

在真实应用中,应当根据错误类型采取适当的恢复策略,而不是直接 break 退出。


6. 总结

  • Stream 适用于异步数据流 ,类似 Iterator,但支持 await。
  • timeout 可为 Stream 每个数据项设置超时时间。
  • throttle 限制 Stream 生成数据的速率。
  • merge 将多个 Stream 合并,便于处理多个数据源。
  • take 限制 Stream 处理的最大数据项数。
  • 合理处理 send 失败,避免异步任务意外崩溃。

🚀 适用场景:

  • 处理 WebSocket 、Kafka 、数据库监听 等 流式数据。
  • 限流 API 调用,避免发送太多请求。
  • 处理用户事件流,如 键盘输入、鼠标点击。

通过 Stream 及其扩展方法,我们可以轻松构建高效的异步数据处理系统。Rust 提供了强大的 async 生态,让我们能更轻松地编写安全、高性能的并发代码!

相关推荐
溜达的大象1 小时前
管家婆远程访问实战:SQL Server 2022配置、PC分机端修改与固定TCP连接
网络·网络协议·tcp/ip
\光辉岁月/1 小时前
15.JavaSE-网络
java·开发语言·网络
星恒讯工业路由器2 小时前
5G RedCap、eRedCap与LTE Cat-1bis:中低速物联网的“三国杀“与选型指南
网络·智能路由器·工业路由器·工业物联网·5g redcap·lte cat-1bis·eredcap
艾芯微科技2 小时前
ETA1637S2G|钰泰同步升压 LED 背光驱动 SOT23-6,0.1% 超低占空比 PWM 调光
网络·单片机·嵌入式硬件·集成测试·51单片机
JosieBook2 小时前
【多线程】两台机器同时抢一个字典:一次并发 Bug 的排查
java·网络·bug
Multipath7122 小时前
以多链路聚合为通信底座,让低空飞行器“连得上、传得快、飞得稳、管得住”
网络·5g·安全·智能路由器·实时音视频
pt10433 小时前
Cisco设备网络安全系列-4:分层网络安全基础设施案例与核心技术
网络·安全
Fu2067213 小时前
网络基本配置
网络
艾芯微科技3 小时前
CJX3134K 长晶双 N 沟道 MOS 管详解|SOT-563 20V 小型电源开关方案
网络·单片机·嵌入式硬件·集成测试·51单片机
白帽攻防录3 小时前
SRC 挖洞:QUIC 协议栈双 RCE,HTTP/3 的新攻击面怎么挖
网络·网络协议·http·网络安全·http/3