在 Rust 中执行此操作的正确方法是什么?

What is the proper way to do this in Rust?

假设 main 上有一个这样的循环:

use crate::modules::very_ext::{one, two};
use crate::modules::much_wow::logic;

let result;
loop {
  let foo = one.next().await.expect("boom");

  // Do something with foo
  result = logic(foo)
}

到目前为止,太棒了。当循环内的逻辑需要对多个外部事件做出“反应”时会发生什么?

如果我们这样做:

use crate::modules::very_ext::{one, two};
use crate::modules::much_wow::logic;

let result;
loop {
  let foo = one.next().await.expect("boom");
  let bar = two.next().await.expect("boom");

  // Do something with foo
  result = logic(foo, bar)
}

然后 logic 只会 运行 之后:

(这不是我们想要的,我们需要在新值出现时做出反应 foobar 已在外部准备好传递给主 循环)。

通过阅读 tokio 的文档,我的猜测是这可以通过使用线程和通道来解决。但我对具体怎么做感到困惑。我是这样想的:

我是不是想多了?有没有更简单的方法(假设多个生产者的一般问题,而不仅仅是两个)?

谢谢!

您可以从两个 future 中生成轻量级任务,这样您就可以等待它们,而无需序列化它们。它在概念上类似于您提出的解决方案,但不使用额外的线程(它在普通执行程序线程上运行 foobar,在它们等待时将它们与其他期货交错),以及通道创建的是为此目的高度优化的一次性通道。

loop {
    // spawn foo and bar to run in the background
    let foo = tokio::spawn(one.next());
    let bar = tokio::spawn(two.next());
    // awaiting foo now also executes bar
    let foo = foo.await.unwrap().expect("boom");
    let bar = bar.await.unwrap().expect("boom");

    result = logic(foo, bar);
}

如果您有两个以上的 Future,您可以使用 futures::future::try_select for this if you have exactly two futures to wait on, or futures::future::select_all - 它们 return 任何剩余未解决的 Futures,因此如果您愿意,您将来仍然可以等待它们。

请注意,select_all 要求所有期货都解析为同一类型,因此您可能需要引入一个包装器 enum,如果它们不同,则每种期货类型都有一个变体。然后你会 map 在调用 select_all.

之前你正在等待的每个 Futures 到它们相关的枚举包装器中