Asynchronous 如何异步处理子流程的I/O?
我有一个子进程,它可能在特定的时间内(例如3秒)写入或不写入它的标准输出 如果子流程stdout中的新行以正确的内容开头,我希望返回该行。 最理想的情况是,我想实现如下目标:Asynchronous 如何异步处理子流程的I/O?,asynchronous,rust,subprocess,Asynchronous,Rust,Subprocess,我有一个子进程,它可能在特定的时间内(例如3秒)写入或不写入它的标准输出 如果子流程stdout中的新行以正确的内容开头,我希望返回该行。 最理想的情况是,我想实现如下目标: use std::io::{BufRead, BufReader}; use std::thread; use std::time::Duration; pub fn wait_for_or_exit( reader: &BufReader<&mut std::process::ChildS
use std::io::{BufRead, BufReader};
use std::thread;
use std::time::Duration;
pub fn wait_for_or_exit(
reader: &BufReader<&mut std::process::ChildStdout>,
wait_time: u64,
cmd: &str,
) -> Option<String> {
let signal: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
let signal_clone = signal.clone();
let child = thread::spawn(move || {
thread::sleep(Duration::from_millis(wait_time));
signal_clone.store(true, Ordering::Relaxed);
});
let mut line = String::new();
while !signal.load(Ordering::Relaxed) {
//Sleep a really small amount of time not to block cpu
thread::sleep(Duration::from_millis(10));
//This line is obviously invalid!
if reader.has_input() {
line.clear();
reader.read_line(&mut line).unwrap();
if line.starts_with(cmd) {
return Some(line);
}
}
}
None
}
使用std::io::{BufRead,BufReader};
使用std::线程;
使用std::time::Duration;
酒吧fn等待或退出(
读者:&BufReader,
等待时间:u64,
cmd:&str,
)->选项{
let信号:Arc=Arc::new(AtomicBool::new(false));
让signal_clone=signal.clone();
让孩子=线程::繁殖(移动| |{
线程::睡眠(持续时间::from_millis(wait_time));
信号存储(true,顺序::Relaxed);
});
让mut line=String::new();
while!signal.load(排序::松弛){
//睡眠时间非常少,不会阻塞cpu
线程::睡眠(持续时间::从_毫秒(10));
//这行显然是无效的!
if reader.has_input(){
line.clear();
reader.read_行(&mut行).unwrap();
如果行以(cmd)开头{
返回一些(行);
}
}
}
没有一个
}
这里唯一不起作用的行是reader.has\u input()
显然,如果子进程的响应速度比等待时间快得多,并且重复了很多次,那么会有很多休眠线程,但我可以通过通道来解决这个问题。有两种方法
use std::thread;
use std::time::Duration;
use std::sync::mpsc;
// this spins up a separate thread in which to wait for stuff to read
// from the BufReader<ChildStdout>
// If we successfully read, we send the string over the Channel.
// Back in the original thread, we wait for an answer over the channel
// or timeout in wait_time secs.
pub fn wait_for_or_exit(
reader: &BufReader<&mut std::process::ChildStdout>,
wait_time: u64,
cmd: &str,
) -> Option<String> {
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
let line = reader.read_line();
sender.send(line);
});
match receiver.recv_timeout(Duration::from_secs(wait_time)) {
Ok(line) => if line.starts_with(cmd)
{ Some(line) } else
{ None },
Err(mpsc::RecvTimeoutError::Timeout) => None,
Err(mpsc::RecvTimeoutError::Disconnected) => None
}
}
我无法编译选项一。在
thread::spawn
中,它抱怨读卡器需要静态生存期。然而,我认为选项一无论如何都不可行,因为底层子进程将被终止并重新启动数百次,并且不能保证它会响应。这将导致数百个侦听器线程,不是吗?我现在正在尝试第二种选择。
extern crate futures;
extern crate tokio;
extern crate tokio_process;
use std::process::Command;
use std::time::{Duration};
use futures::Future;
use tokio_process::CommandExt;
use tokio::prelude::*;
const TIMEOUT_SECS: u64 = 3;
fn main() {
// Like above, but use `output_async` which returns a future instead of
// immediately returning the `Child`.
let output = Command::new("echo").arg("hello").arg("world")
.output_async();
let future = output.map_err(|e| panic!("failed to collect output: {}", e))
.map(|output| {
assert!(output.status.success());
assert_eq!(output.stdout, b"hello world\n");
println!("received output: {}", String::from_utf8(output.stdout).unwrap());
})
.timeout(Duration::from_secs(TIMEOUT_SECS)) // here is where we say we only want to wait TIMETOUT seconds
.map_err(|_e| { println!("Timed out waiting for data"); });
tokio::run(future);
}