【问题标题】:Read Stdout and Stderr parallel and kill process on timeout并行读取 Stdout 和 Stderr 并在超时时终止进程
【发布时间】:2021-06-19 04:50:39
【问题描述】:

我需要与另一个进程交互。 我想使用两个线程,一个用于在 stdout 中读取,一个用于在 stderr 上读取,这些线程将读取的每一行传递到一个通道。 主线程收集行并检查超时,如果发生超时,则应终止进程。

我的问题是如何共享 std-out/err 句柄,并且在发生超时时仍然能够在以后终止进程。

    let mut stdout = process.stdout.unwrap();
    let (tx_stdout, rx_stdout): (Sender<Result<Vec<u8>, ExecutionError>>, Receiver<Result<Vec<u8>, ExecutionError>>) = mpsc::channel();
    std::thread::spawn(move || {
        PowerShell::read_lines(&tx_stdout, stdout);
    });

    let mut stderr = process.stderr.unwrap();
    let (tx_stderr, rx_stderr): (Sender<Result<Vec<u8>, ExecutionError>>, Receiver<Result<Vec<u8>, ExecutionError>>) = mpsc::channel();
    std::thread::spawn(move || {
        PowerShell::read_lines(&tx_stderr, &mut stderr);
    });

   process.kill(); // this is not possible

我明白为什么编译器会出现问题,但我不知道如何解决这个问题。 正确使用 Arc 会导致同样的问题。

你对我有什么建议吗?

【问题讨论】:

    标签: multithreading rust


    【解决方案1】:

    首先,在寻求 Rust 帮助时,有两件事非常有用和有用,那就是设置一个简化的测试用例(如果可能)并将其发布到 https://play.rust-lang.org,然后阅读并发布 编译错误。

    这里是简化的测试用例would be this

    use std::process::{Command, Stdio};
    
    fn main() {
        let mut process = Command::new("echo")
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn().expect("Failed to start echo process");
    
        let stdout = process.stdout.unwrap();
        std::thread::spawn(move || {
            drop(stdout);
        });
    
        let stderr = process.stderr.unwrap();
        std::thread::spawn(move || {
            drop(stderr);
        });
    
       process.kill(); // this is not possible
    }
    

    编译错误是

    error[E0382]: borrow of partially moved value: `process`
       --> src/lib.rs:19:4
        |
    14  |     let stderr = process.stderr.unwrap();
        |                                 -------- `process.stderr` partially moved due to this method call
    ...
    19  |    process.kill(); // this is not possible
        |    ^^^^^^^ value borrowed here after partial move
        |
    note: this function consumes the receiver `self` by taking ownership of it, which moves `process.stderr`
    

    这很清楚:process.stderr 是一个OptionOption::unwrap 具有以下签名:

    pub fn unwrap(self) -> T
    

    表示它的主题按价值。在两次展开调用之后,process 对象的含义不再有效:它已被部分剥离。某些部分仍然可用,但只有在已知对象有效时才能调用方法,此处并非如此。

    因此出现“部分移动”编译错误,您不能将某些内容剥离为部分并仍按原样使用它。好吧,也许你可以依赖于部分,但是 Rust 没有表达部分依赖,所以编译器必须假设 Child::kill 需要孩子活着并且很好,而不仅仅是一些位。

    根据具体情况有多种可能性:在某些情况下,您可以复制或克隆您想要的位,或者您可以使用作用域线程来借用它们。这些都不可能在这里,但仍有可能:Option::take 允许您获取选项的 content,并将其替换为 None。所以在这里你可以将ChildStd*移出结构*同时保持结构不变`,这只需要修改结构的能力:

    use std::process::{Command, Stdio};
    
    fn main() {
        let mut process = Command::new("echo")
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn().expect("Failed to start echo process");
    
        let stdout = process.stdout.take().unwrap();
        std::thread::spawn(move || {
            drop(stdout);
        });
    
        let stderr = process.stderr.take().unwrap();
        std::thread::spawn(move || {
            drop(stderr);
        });
    
       process.kill(); // this is not possible
    }
    

    【讨论】:

    • 非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    • 2010-12-06
    • 2015-09-13
    • 2022-12-07
    相关资源
    最近更新 更多