我是 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/