Rust tokio 无限循环用于多个按钮监听

标签 rust rust-tokio

我是 Rust 新手,我很难理解如何使用 tokio无限循环中的阻塞代码。我的 Raspberry Pi 3 连接了两个按钮,我想听任一按钮被按下的声音。我正在使用rust_gpiozero用于此的 crate 。

这是按钮代码:

use rust_gpiozero::*;
let mut button = Button::new(19);
button.wait_for_press(None); // blocking here

我不知道如何连续监听主代码中的任一按钮。我想我应该使用 tokio::task::spawn_blocking但我不知道怎么做。像这样的事情:

#[tokio::main]
async fn main() {
  let b1_blocking_task = tokio::task::spawn_blocking(|| {
    let mut button = Button::new(19);
    button.wait_for_press(None); // blocks here
  });

  let b2_blocking_task = tokio::task::spawn_blocking(|| {
    let mut button = Button::new(26);
    button.wait_for_press(None); // blocks here
  });

  loop { // forever listen for button presses
     tokio::select! {
       _ = b1_blocking_task => {
           println!("Button 1 pressed")
       }
       _ = b2_blocking_task => {
           println!("Button 2 pressed")
       }
     };
  }
}

上面的代码不起作用,但是正确执行此操作的最佳策略是什么?

最佳答案

对代码进行一点小小的更改即可使其正常工作:

#[tokio::main]
async fn main() {
    let b1_blocking_task = || {
        tokio::task::spawn_blocking(|| {
            let mut button = Button::new(19);
            button.wait_for_press(None); // blocks here
        })
    };

    let b2_blocking_task = || {
        tokio::task::spawn_blocking(|| {
            let mut button = Button::new(26);
            button.wait_for_press(None); // blocks here
        })
    };

    loop {
        // forever listen for button presses
        tokio::select! {
          _ = b1_blocking_task() => {
              println!("Button 1 pressed")
          }
          _ = b2_blocking_task() => {
              println!("Button 2 pressed")
          }
        };
    }
}

这将为每次按下按钮生成一个新线程。它不是特别有效,但如果这些是用户输入事件,也可能不太重要。

您可以通过仅生成每个线程一次并使用 channel 在它们之间进行通信来使其变得更好:

use tokio::sync::mpsc::channel;

#[tokio::main]
async fn main() {
    // pick suitable queue sizes for these channels
    let (mut b1_sender, mut b1_receiver) = channel(16);
    let (mut b2_sender, mut b2_receiver) = channel(16);

    let b1_blocking_task = tokio::task::spawn_blocking(|| {
        let mut button = Button::new(19);
        button.wait_for_press(None); // blocks here
        // will panic if the channel queue gets full
        b1_sender.try_send(()).unwrap();
    });

    let b2_blocking_task = tokio::task::spawn_blocking(|| {
        let mut button = Button::new(26);
        button.wait_for_press(None); // blocks here
        // will panic if the channel queue gets full
        b2_sender.try_send(()).unwrap();
    });

    let (_, _, _) = tokio::join!(
        b1_blocking_task,
        b2_blocking_task,
        tokio::task::spawn(async move {
            loop {
                // forever listen for button presses
                tokio::select! {
                    Some(_) = b1_receiver.recv() => {
                        println!("Button 1 pressed")
                    }
                    Some(_) = b2_receiver.recv() => {
                        println!("Button 2 pressed")
                    }
                    else => break
                };
            }
        })
    );
}

您使用的 API 并不理想,因为它要求您创建阻塞线程。如果有某种方法可以轮询按钮以查看它是否已被单击,那就更好了。在内部, crate 似乎正在这样做,因此它应该有可能公开该机制,这将使您进一步改进。

关于Rust tokio 无限循环用于多个按钮监听,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62376239/

相关文章:

rust - 当我有一个大的 HashMap 字面量时,我该如何改进编译时间?

rust - 为什么闭包会引入借用,而内联的相同代码却不会引入借用?

rust - Hyper 中的共享可变状态

rust - 为什么迭代器的实现在异步上下文中不够通用?

rust - 如何使用带有异步的指示器显示总计数器栏?

rust - 发送到数组中的每个 futures::sync::mpsc::Sender

namespaces - 为什么我不能在 Trait impl block 的 match Arms 中使用 Self?

rust - 如何在 Rust 中对 'Option' 分配进行分组?

rust - 传递给 reqwest::multipart::Part::reader 的内容

rust - 如何解决 "the trait bound ` [closure]: tokio::prelude::Future` is not satisfied"when calling tokio::spawn?