简体   繁体   English

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

[英]What is the proper way to do this in Rust?

Suppose a loop on main like this:假设 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)
}

This far, great.这么远,太好了。 What happens when the logic inside the loop needs to "react" to multiple external events?当循环内部的逻辑需要对多个外部事件“做出反应”时会发生什么?

If we do something like this:如果我们这样做:

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)
}

then logic will only run after:那么logic只会在以下之后运行:

  • foo resolves first foo首先解析
  • then bar eventually resolves as well然后bar最终也会解决

(which is not what we want, we need to react whenever a new value for either foo or bar is externally ready to be passed to the main loop). (这不是我们想要的,每当foobar的新值在外部准备好传递给主循环时,我们都需要做出反应)。

By reading tokio 's docs, my guess is that this can be solved by using threads and channels.通过阅读tokio的文档,我的猜测是这可以通过使用线程和通道来解决。 But I'm confused on exactly how.但我对究竟如何感到困惑。 I am thinking it like this:我是这样想的:

  • Create an mpsc channel.创建一个mpsc频道。
  • Spawn one and two into their own threads.onetwo生成到它们自己的线程中。
  • Each thread pushes a value on the channel with perhaps some encapsulation that distinguishes it.每个线程在通道上推送一个值,可能有一些区分它的封装。
  • The main loop is the single consumer that "reacts" as the values come in over the mpsc channel.主循环是在值通过mpsc通道进入时“做出反应”的单一消费者。

Am I overthinking this?这是我想太多了吗? Is there a simpler way (assume the general problem with multiple producers, not just two)?有没有更简单的方法(假设有多个生产者的一般问题,而不仅仅是两个)?

Thank you!谢谢!

You can spawn lightweight tasks out of both futures, which will allow you to await them both without serializing them.您可以从两个期货中生成轻量级任务,这将允许您在不序列化它们的情况下等待它们。 It is conceptually similar to the solution you proposed, but doesn't use additional threads (it runs foo and bar on the normal executor threads, interleaving them with other futures as they await), and the channels created are one-shot channels highly optimized for this purpose.它在概念上与您提出的解决方案相似,但不使用额外的线程(它在普通执行线程上运行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);
}

You can use futures::future::try_select for this if you have exactly two futures to wait on, or futures::future::select_all if you have more than two - they return any remaining unresolved Futures so you can still wait on them in the future if you want.如果您有两个以上的期货要等待,则可以使用futures::future::try_select ,如果您有两个以上的期货,则可以使用 futures::future:: futures::future::select_all - 它们返回任何剩余的未解决期货,因此您仍然可以等待它们如果你愿意,在未来。

Note that select_all requires that all futures resolve to the same type, so you may need to introduce a wrapper enum with one variant per type of your futures if they are different.请注意, select_all要求所有期货都解析为相同的类型,因此如果它们不同,您可能需要为每种期货类型引入一个包含一个变体的包装enum Then you would map each of the Futures you're awaiting into their relevant enum wrapper before calling select_all .然后,在调用select_all之前,您将map您正在等待的每个期货放入其相关的枚举包装器中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM