multithreading - 在 Rust 命令过程中写入 stdio 并从 stdout 读取
问题描述
我将尝试尽可能简化我想要完成的工作,但简而言之,这是我的问题:
我正在尝试将节点外壳生成为 Rust 中的一个进程。我想传递给进程的标准输入 javascript 代码并从进程的标准输出读取 nodejs 输出。这将是一种交互式用法,其中节点 shell 被生成并不断接收 JS 指令并执行它们。
我不希望使用文件参数启动 nodejs 应用程序。
我已经阅读了很多关于 std::process::Command、tokio 以及为什么我们不能使用标准库对管道输入进行读写的内容。我一直在网上看到的一种解决方案(为了在读/写时不阻塞主线程)是使用线程来读取输出。大多数解决方案不涉及连续的写入/读取流程。
我所做的是生成 2 个线程,一个继续写入标准输入,一个继续从标准输出读取。这样,我想,我不会阻塞主线程。但是我的问题是只能主动使用 1 个线程。当我有一个标准输入线程时,标准输出甚至不接收数据。
这是代码,注释应提供更多详细信息
pub struct Runner {
handle: Child,
pub input: Arc<Mutex<String>>,
pub output: Arc<Mutex<String>>,
input_thread: JoinHandle<()>,
output_thread: JoinHandle<()>,
}
impl Runner {
pub fn new() -> Runner {
let mut handle = Command::new("node")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to spawn node process!");
// begin stdout thread part
let mut stdout = handle.stdout.take().unwrap();
let output = Arc::new(Mutex::new(String::new()));
let out_clone = Arc::clone(&output);
let output_thread = spawn(move || loop {
// code here never executes...why ?
let mut buf: [u8; 1] = [0];
let mut output = out_clone.lock().unwrap();
let what_i_read = stdout.read(&mut buf);
println!("reading: {:?}", what_i_read);
match what_i_read {
Err(err) => {
println!("{}] Error reading from stream: {}", line!(), err);
break;
}
Ok(bytes_read) => {
if bytes_read != 0 {
let char = String::from_utf8(buf.to_vec()).unwrap();
output.push_str(char.as_str());
} else if output.len() != 0 {
println!("result: {}", output);
out_clone.lock().unwrap().clear();
}
}
}
});
// begin stdin thread block
let mut stdin = handle.stdin.take().unwrap();
let input = Arc::new(Mutex::new(String::new()));
let input_clone = Arc::clone(&input);
let input_thread = spawn(move || loop {
let mut in_text = input_clone.lock().unwrap();
if in_text.len() != 0 {
println!("writing: {}", in_text);
stdin.write_all(in_text.as_bytes()).expect("!write");
stdin.write_all("\n".as_bytes()).expect("!write");
in_text.clear();
}
});
Runner {
handle,
input,
output,
input_thread,
output_thread,
}
}
// this function should receive commands
pub fn execute(&mut self, str: &str) {
let input = Arc::clone(&self.input);
let mut input = input.lock().unwrap();
input.push_str(str);
}
}
在主线程中,我想将其用作
let mut runner = Runner::new();
runner.execute("console.log('foo'");
println!("{:?}", runner.output);
我还是 Rust 的新手,但至少我通过了借用检查器让我头撞墙的地步,我现在开始觉得它更令人愉悦了 :)
解决方案
你是否在线程中设置了一个循环来等待输入?如何等待程序结束。
你现在,如果你这样做
use std::{thread};
fn main() {
let _output_thread = thread::spawn(move || {
println!("done");
});
}
程序的结束有时线程更快,您看不到输出“完成”。
如果你做这样的循环,是一样的
use std::{thread};
fn main() {
//let output_thread = spawn(move || loop {
// code here never executes...why ?
let _output_thread = thread::spawn(move || {
//-------------------------------------^---
loop {
println!("done");
}
});
}
如果你等待它工作得很好。
use std::{thread, time};
fn main() {
//let output_thread = spawn(move || loop {
// code here never executes...why ?
let _output_thread = thread::spawn(move || {
//-------------------------------------^---
loop {
println!("done");
}
});
thread::sleep(time::Duration::from_secs(5));
}
使用 2 胎面的一种好方法就像在https://doc.rust-lang.org/std/thread/fn.spawn.html中阐述的那样,使用通道并加入
use std::thread;
use std::sync::mpsc::channel;
let (tx, rx) = channel();
let sender = thread::spawn(move || {
tx.send("Hello, thread".to_owned())
.expect("Unable to send on channel");
});
let receiver = thread::spawn(move || {
let value = rx.recv().expect("Unable to receive from channel");
println!("{}", value);
});
sender.join().expect("The sender thread has panicked");
receiver.join().expect("The receiver thread has panicked");
推荐阅读
- apache-kafka - 在pyflink中访问kafka时间戳
- python - 需要帮助在 python 中为每项投资在不同日期计算不同投资的 IRR
- node.js - 使用节点 JS 发出 Firebase 云函数调用第三方 API
- c++ - C++;While循环无法退出;哨兵似乎没有被认出来。带有 Switch 语句的嵌套循环
- python - 如何在 Pyspark 中填写空值
- python - 无法理解奇怪的 Python lambda 函数
- python - 如何避免重复 Python 子类的文档字符串和参数?
- python - wxPython - 为当前作为 CLI 版本工作的应用程序创建 GUI - 不知道从哪里开始
- python - 使用 odeint 在 Python 中解决 Spring-mass 系统
- javascript - Firebase 网络推送已收到但未显示