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

Rust tokio无限循环用于多按钮监听,rust,rust-tokio,Rust,Rust Tokio,我是个新手,我正在努力理解如何使用tokio在无限循环中阻塞代码。我有两个按钮连接到我的树莓皮3,我想听到任何一个按钮被按下。我用的是rust\gpiozero板条箱 以下是按钮代码: 使用RUSTgpiozero::*; 让mut按钮=按钮::新(19); 按钮。等待按(无);//在这里阻塞 我不知道如何连续地监听主代码中的任何一个按钮。我想我应该使用tokio::task::spawn_blocking,但我不确定如何使用。大概是这样的: #[tokio::main] 异步fn main(

我是个新手,我正在努力理解如何使用
tokio
在无限循环中阻塞代码。我有两个按钮连接到我的树莓皮3,我想听到任何一个按钮被按下。我用的是
rust\gpiozero
板条箱

以下是按钮代码:

使用RUSTgpiozero::*;
让mut按钮=按钮::新(19);
按钮。等待按(无);//在这里阻塞
我不知道如何连续地监听主代码中的任何一个按钮。我想我应该使用
tokio::task::spawn_blocking
,但我不确定如何使用。大概是这样的:

#[tokio::main]
异步fn main(){
让b1_blocking_task=tokio::task::spawn_blocking(| |){
让mut按钮=按钮::新(19);
按钮。等待按(无);//在此处阻塞
});
让b2_blocking_task=tokio::task::spawn_blocking(| |){
让mut按钮=按钮::新(26);
按钮。等待按(无);//在此处阻塞
});
循环{//永远听按钮按下
东京:选择{
_=b1_阻塞_任务=>{
println!(“按下按钮1”)
}
_=b2_阻塞_任务=>{
println!(“按下按钮2”)
}
};
}
}

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

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

#[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")
          }
        };
    }
}
这将为每次按下按钮产生一个新线程。这不是特别有效,但如果这些都是用户输入事件,可能也不太重要

只需生成每个线程一次,并使用通道在它们之间进行通信,就可以使其变得更好:

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并不理想,因为它要求您创建阻塞线程。如果有一种方法来轮询按钮以查看是否被单击,那会更好。在内部,板条箱似乎正在这样做,因此它应该可以公开该机制,这将让您在这方面进一步改进。

对您的代码进行一个小的更改将使其工作:

#[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")
          }
        };
    }
}
这将为每次按下按钮产生一个新线程。这不是特别有效,但如果这些都是用户输入事件,可能也不太重要

只需生成每个线程一次,并使用通道在它们之间进行通信,就可以使其变得更好:

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并不理想,因为它要求您创建阻塞线程。如果有一种方法来轮询按钮以查看是否被单击,那会更好。在内部,板条箱似乎正在这样做,因此它应该有可能暴露该机制,这将让您在这方面进一步改进。

在您衍生的任务中使用通道和循环。您好,星门,感谢您的响应。我不确定我是否明白,你的意思是把每个
wait\u for\u press
函数放在它自己的循环{}中,之后有一个通道发送调用?在生成的任务中使用一个通道和循环。您好,Stargateur,感谢您的响应。我不确定我是否遵循,你的意思是将每个
wait\u for\u press
函数放在自己的循环{}中,之后使用一个频道发送调用?谢谢Peter,我怀疑板条箱的设计有点奇怪,但不确定,因为我缺乏生锈方面的经验。我会找更好的。仅供参考,由于按钮多次初始化,代码恐慌,说
PinNotAvailable(19)
,但该策略似乎有效。第二段代码连续打印
按钮1按下
按钮2按下
(我不按任何一个按钮)当我在我的PI3.0线程上运行它时,可能会出现恐慌,所以未来已经准备好了,并且报告没有。我不确定使用try_send是否有意义。谢谢Peter,我怀疑板条箱的设计有点奇怪,但不确定,因为我缺乏生锈方面的经验。我会找更好的。仅供参考,由于按钮多次初始化,代码恐慌,说
PinNotAvailable(19)
,但该策略似乎有效。第二段代码连续打印
按钮1按下
按钮2按下
(我不按任何一个按钮)当我在我的PI3.0线程上运行它时,可能会出现恐慌,所以未来已经准备好了,并且报告没有。我不确定使用try\u send是否有意义。