在 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
只会 运行 之后:
foo
先解决
- 然后
bar
最终也解决了
(这不是我们想要的,我们需要在新值出现时做出反应
foo
或 bar
已在外部准备好传递给主
循环)。
通过阅读 tokio
的文档,我的猜测是这可以通过使用线程和通道来解决。但我对具体怎么做感到困惑。我是这样想的:
- 创建一个
mpsc
频道。
- 将
one
和 two
生成到它们自己的线程中。
- 每个线程在通道上推送一个值,可能带有一些区分它的封装。
- 主循环是单个消费者,它在值通过
mpsc
通道传入时“做出反应”。
我是不是想多了?有没有更简单的方法(假设多个生产者的一般问题,而不仅仅是两个)?
谢谢!
您可以从两个 future 中生成轻量级任务,这样您就可以等待它们,而无需序列化它们。它在概念上类似于您提出的解决方案,但不使用额外的线程(它在普通执行程序线程上运行 foo
和 bar
,在它们等待时将它们与其他期货交错),以及通道创建的是为此目的高度优化的一次性通道。
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 到它们相关的枚举包装器中
假设 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
只会 运行 之后:
foo
先解决- 然后
bar
最终也解决了
(这不是我们想要的,我们需要在新值出现时做出反应
foo
或 bar
已在外部准备好传递给主
循环)。
通过阅读 tokio
的文档,我的猜测是这可以通过使用线程和通道来解决。但我对具体怎么做感到困惑。我是这样想的:
- 创建一个
mpsc
频道。 - 将
one
和two
生成到它们自己的线程中。 - 每个线程在通道上推送一个值,可能带有一些区分它的封装。
- 主循环是单个消费者,它在值通过
mpsc
通道传入时“做出反应”。
我是不是想多了?有没有更简单的方法(假设多个生产者的一般问题,而不仅仅是两个)?
谢谢!
您可以从两个 future 中生成轻量级任务,这样您就可以等待它们,而无需序列化它们。它在概念上类似于您提出的解决方案,但不使用额外的线程(它在普通执行程序线程上运行 foo
和 bar
,在它们等待时将它们与其他期货交错),以及通道创建的是为此目的高度优化的一次性通道。
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
.