如何让我的程序长时间睡眠,同时保持响应能力



我正在做一个基础项目。我(第一次(进步了很多。我在尝试将其作为一个工作应用程序时遇到了一个障碍。

我的意思是,它有一个主循环,它是无限的。程序将运行,直到用户按下q。顺便说一下,这个程序是控制台应用程序。我正在研究Linux。

当程序工作时,它需要每隔一段时间(例如1小时、30分钟、4小时(进行一些操作foo

是的,就是这样。我知道这听起来并不复杂,但我被卡住了。所以我想要的是一个Stdin,它在不阻塞应用程序的情况下等待用户输入,在此期间每1小时执行foo(让我们坚持一个小时(。但在这些foo之间,我不希望我的程序不断旋转。因为这会导致100%的CPU使用率。我希望我的程序在需要工作时工作(例如,当运行时间为1小时或当用户按下q时(。

我用了一个库来操作终端。该库具有Stdin的无阻塞功能。我查看了它的实现,发现它创建了另一个线程来处理这个问题。因为我必须自己做其他功能。我需要一些帮助来弄清楚。

因为时光流逝而不旋转。我尝试了sleep主线程。它确实有所帮助,但它使用户输入没有响应。如果用户在主线程休眠时按下q。它在等待它醒来。我不想要这个。如果它闲置,我希望它立即退出。(不执行foo(。如果正在执行foo,则在执行foo之后退出。顺便说一句,foo在我的代码中不是一个繁重的计算。

fn foo() {
println!("bar");
}
fn main() {
let mut instant = Instant::now();
let one_hour = Duration::from_secs(60 * 60);
let mut stdin = async_stdin().bytes();
loop {
// No need to work
std::thread::sleep(one_hour);
// Just to be sure
if instant.elapsed() > one_hour {
foo();
instant = Instant::now();
}
// Check for input if there is any
match stdin.next() {
Some(Ok(b'q')) => break,
_ => (),
}
}
}
foo  stdin                          foo   stdin
|     |                             |      |
|     |                             |      | <- Press q
|     |                             |      |
|                                    | <- exit here
|<- Press q and      or              |
|   exit immediately                 |
|                                    |
|                                   |
|                                   |
|                                   |

我的项目使用Rust,但伪代码对我来说很好。我对多线程和异步的概念很陌生。我知道他们不一样。我甚至不知道这需要哪一个。我需要哪些同步对象?任何提示、参考资料和链接都会有所帮助。

这里有一个使用tokio的示例。

use std::io::{stdin, BufRead, Result};
use tokio::time::{interval, Duration};
#[tokio::main]
async fn main() -> Result<()> {
tokio::spawn(async {
let mut interval = interval(Duration::from_secs(60 * 60)); // one hour
interval.tick().await; // first tick fires immediately, ignore it
loop {
interval.tick().await;
// do your stuff
}
});
for line in stdin().lock().lines() {
if line? == String::from("q") {
break;
}
}
Ok(())
}

第一个位生成一个async任务,该任务将永远循环,在两个节拍之间等待一个小时。而第二个比特只是阻止用户输入。输入"q"会立即退出main并破坏async执行器。如果任务正在处理过程中,它将等待它到达下一个await,然后再退出。

您需要运行2个线程并传递有关退出的消息,然后等待它们超时。

use std::sync::mpsc;
use std::thread;
type Signal = ();
fn work_queue(end_receiver: mpsc::Receiver<Signal>){
loop{
foo();
match end_receiver.recv_timeout(one_hour){
Ok(_) => return, // Received signal
Err(mpsc::RecvTimeoutError::Timeout)=>foo(),
_ => handle_error(),
}     
}
}
fn main(){
let (send, recv) = channel();

thread::spawn(move || {
work_queue(recv);
});
loop{
match stdin.next() {
Some(Ok(b'q')) => {
send.send(()).unwrap();
break;
},
_ => continue,
}
}
}

我没有测试这个代码,但我希望这个想法是清楚的。

我也是新手,我很想弄清楚你的问题。想出这个答案我学到了很多。这个答案建立在公认答案的基础上。

以下是一个完整且有效的示例,它解决了读取键盘的一个常见问题:在读取bytes()之前必须按下回车键

我还为mpsc创建了多个(两个(事件生成器,并添加了计时器机箱来处理计时。

注意:这可能无法在vscode或jetbrains环境终端中正常运行,因为它依赖于termion。在真正的终端中运行它。

来自cargo.toml的依赖性

[dependencies]
timer = "0.2.0"
chrono = "0.4.19"
termion = "1.5.6"

main.rs

extern crate timer;
extern crate chrono;
extern crate termion;
use std::thread;
use std::sync::mpsc::channel;
use termion::input::TermRead;
use std::io::{stdout, stdin};
use termion::raw::IntoRawMode;
use termion::event::Key;
enum Events { TimerEvent, QuitEvent }
pub fn main() {
let (tx, rx) = channel();
let timer = timer::Timer::new();
let timer_tx = tx.clone();
let _guard = timer.schedule_repeating(chrono::Duration::seconds(3), move || {
timer_tx.send(Events::TimerEvent).unwrap();
});
thread::spawn(move || {
//This is the trick to reading input one key at a time
stdout().into_raw_mode().unwrap();
for input in stdin().keys() {
if input.unwrap() == Key::Char('q') {
tx.send(Events::QuitEvent).unwrap();
}
}
});
loop {
let value = rx.recv().unwrap();
match value {
Events::TimerEvent => work_fn(),
Events::QuitEvent => return  // return from main()
}
}
}
fn work_fn() {
println!("Timer went off! Do work.");
}

最新更新