简体   繁体   English

并行执行外部命令,将output捕获到Rust数组中

[英]Executing external commands in parallel and capturing the output in an array in Rust

I have the following while loop that runs generate_user_key for each of the file in the file_array , and outputs the result.我有以下 while 循环,它为file_array中的每个文件运行generate_user_key并输出结果。 I would like to parallelize this such that an array of the generated keys is returned, and the process is executed in parallel instead of sequential to make it faster.我想将其并行化,以便返回生成的键的数组,并且并行执行该过程而不是顺序执行以使其更快。

use std::process::Command;

//file_array definition here

let mut i = 0;
while (i<100) {
  let generated_key = Command::new("generate_user_key")
                       .arg(file_array[i])
                       .output()
                       .expect("generate_user_key command failed to start");
  println!("stdout: {}", String::from_utf8_lossy(&generated_key.stdout));
  i=i+1;
}

What is the best way to implement this in rust?在 rust 中实现这个的最佳方法是什么?

If you want to loop over the array items using rayon then you can simply create into_par_iter and work on array items如果你想使用rayon遍历数组项,那么你可以简单地创建into_par_iter并处理数组项

use std::process::Command;
use rayon::iter::{ParallelIterator, IntoParallelIterator};

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let result: Vec<_> = arr.into_par_iter().flat_map(|value| {
        let output = Command::new("sh")
                .args(["-c", &format!("echo {}", value)])
                .output()
                .expect("failed to execute process");
        println!("Index: {}, Output: {:?}", value, output.stdout);
        output.stdout
    });

    println!("{:?}", result);
}

You can also use range to loop over and use the counter as array index您还可以使用range循环并将counter用作array索引

use std::process::Command;
use rayon::iter::{ParallelIterator, IntoParallelIterator};

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let result: Vec<_> = (0..arr.len()).into_par_iter().flat_map(|idx| {
        let output = Command::new("sh")
                .args(["-c", &format!("echo {}", arr[idx])])
                .output()
                .expect("failed to execute process");
        println!("Index: {}, Output: {:?}", idx, output.stdout);
        output.stdout
    });

    println!("{:?}", result);
}

Example using thread使用thread的例子

use std::thread;
use std::time::Duration;

fn main() {
    let mut threads = vec![];
    for idx in 0..arr.len() {
        threads.push(thread::spawn(move || -> Vec<_> {
            let output = Command::new("sh")
                    .args(["-c", &format!("echo -n {}", idx)])
                    .output()
                    .expect("failed to execute process");
            println!("Index: {}, Output: {:?}", idx, output.stdout);
            thread::sleep(Duration::from_millis(1));
            output.stdout
        }));
    }

    let result = threads.into_iter().flat_map(|c| c.join().unwrap()).collect::<Vec<_>>();

    println!("{:?}", result);
}

This should be easy to do with rayon .这对人造丝应该很容易做到。 Eg something like this (untested since I don't have your generate_user_key ):例如这样的东西(未经测试,因为我没有你的generate_user_key ):

use rayon::prelude::*;
let keys = (0..100).into_par_iter().map (|_| {
        Command::new("generate_user_key")
            .arg(file_array[i])
            .output()
            .expect("generate_user_key command failed to start")
            .stdout
    })
    .collect::<Vec<_>>();

or better:或更好:

use rayon::prelude::*;
let keys = file_array.par_iter().map (|f| {
        Command::new("generate_user_key")
            .arg(f)
            .output()
            .expect("generate_user_key command failed to start")
            .stdout
    })
    .collect::<Vec<_>>();

When all else fails, throw threads at the problem.当所有其他方法都失败时,将问题抛出线程。 It almost certainly isn't the correct approach, but it works.这几乎肯定不是正确的方法,但它确实有效。

let mut join_handles = Vec::new();

for _ in 0..100 {
    join_handles.push(thread::spawn(|| {
        let generated_key = Command::new("generate_user_key")
                              .arg(file_array[i])
                              .output()
                              .expect("generate_user_key command failed to start");

        String::from_utf8_lossy(&generated_key.stdout)
    }));
}

let outputs = join_handles.into_iter().map(Result::unwrap).collect::<Vec<_>>();

Edit: The correct solution is probably using Command::spawn to start the processes without blocking.编辑:正确的解决方案可能是使用Command::spawn在不阻塞的情况下启动进程。 The OS can then handle running them in parallel and you can then collect the outputs.然后操作系统可以处理并行运行它们,然后您可以收集输出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM