【问题标题】:Writing to stdio & reading from stdout in Rust Command process在 Rust 命令过程中写入 stdio 并从 stdout 读取
【发布时间】:2021-11-10 23:47:14
【问题描述】:

我会尽量简化我想要完成的工作,但简而言之,这是我的问题:

我正在尝试将节点 shell 作为 Rust 中的进程生成。我想传递给进程的标准输入 javascript 代码并从进程的标准输出读取 nodejs 输出。这将是一种交互式用法,其中节点 shell 被生成并不断接收 JS 指令并执行它们。

我不希望使用文件参数启动 nodejs 应用程序。

我已经阅读了很多关于 std::process::Command、tokio 以及为什么我们不能使用标准库对管道输入进行读写的内容。我一直在网上看到的一种解决方案(为了在读/写时不阻塞主线程)是使用线程来读取输出。大多数解决方案不涉及连续的写入/读取流程。

我所做的是生成 2 个线程,一个继续写入标准输入,一个继续从标准输出读取。这样,我想,我不会阻塞主线程。但是我的问题是只能主动使用 1 个线程。当我有一个标准输入线程时,标准输出甚至不接收数据。

这里是代码,cmets应该提供更多细节

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 的新手,但至少我已经过了借用检查器让我头撞墙的地步,我现在开始觉得它更令人愉悦了 :)

【问题讨论】:

    标签: multithreading rust stdout stdin


    【解决方案1】:

    你是否在线程中设置了一个循环来等待输入?如何等待程序结束。

    你现在,如果你这样做了

    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");
    

    【讨论】:

      猜你喜欢
      • 2018-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-05
      • 1970-01-01
      • 2017-08-16
      相关资源
      最近更新 更多