一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Rust并发编程入门:多进程创建与等待子进程的实用指南

时间:2026-10-19 08:36:01 编辑:袖梨 来源:一聚教程网

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“Rust并发编程入门:多进程创建与等待子进程”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

Rust的并发编程

并发,是指在宏观意义上同一时间处理多个任务。

并发的方式一般包含为三种:

多进程、多线程以及最近几年刚刚火起来的协程。

多进程并发

新建子进程

首先,我们新建两个项目,一个为子进程,一个为主进程。

在子进程的main.rs里,编写如下所示代码:

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

fn main() {
    println!("Hello, world!");
    sleep(Duration::from_secs(5)); // 休息5秒钟
    println!("Bye, world!");
}

在这个场景下,程序先输出"Hello, world!",随后休息5秒钟,最后打印"Bye, world!"退出。

在主进程的main.rs里,编写如下所示代码:

use std::process::Command;

fn main() {
    Command::new("../sub/target/debug/sub.exe").spawn().unwrap();
}

落到代码里,执行主进程时,发现子进程输出了"Hello, world!",但是没有下文了。

结合项目来看,有经验的我们都知道,这是因为主进程启动了子进程后立刻退出了。我们需等待子进程结束。

等待子进程结束

在这个场景下,要等待子进程结束,需采用一个变量保存子进程对象,随后调用子进程的wait方法:

use std::process::Command;

fn main() {
    let mut p = Command::new("../sub/target/debug/sub.exe").spawn().unwrap();
    p.wait().unwrap();
}

结合项目来看,这里需留意,因为wait方法会改变子进程对象的状态,所以子进程对象必须是可变的。

在这个场景下,与wait对应的,还有一个try_wait方法,try_wait不会阻塞主进程,无论子进程是否结束,都会得到Result,同时将状态保存到Ok中:

use std::thread::sleep;
use std::time::Duration;
use std::process::Command;

fn main() {
    let mut p = Command::new("../sub/target/debug/sub.exe").spawn().unwrap();
    loop {
        let status = p.try_wait().unwrap();
        match status {
            Some(status) => {
                if status.success() {
                    println!("sub process over");
                    
                } else {
                    println!("sub process error");
                }
                break;
            }

            None => {
                sleep(Duration::from_secs(1));
                continue;
            }
        }
    }
}

结合项目来看,Ok中保存是一个Option,当子进程结束时,为Some,没有进程时为None。

指定子进程的命令行参数

修改一下子进程,使子进程能够借助命令行参数来决定sleep的时间。

接收命令行参数能够采用标准库的env模块:

use std::env::args;
use std::thread::sleep;
use std::time::Duration;

fn main() {
    println!("Hello, world!");
    let mut args = args();
    let secs = args.nth(1).unwrap().parse::<u64>().unwrap();

    sleep(Duration::from_secs(secs));
    println!("Bye, world!");
}

从实现思路看,args得到一个比较复杂的结构,能够用nth来取得第几个参数的值,第0个是exe的名称,第1个是传给exe的第1个参数。因为nth有可能是None,所以得到的是Option,能够用unwrap()取得参数的字符串。

Duration::from_secs接收的是u64类型,需把字符串采用parse::()转成u64类型,转换过程可能会失败,所以parse得到Result,同样能够采用unwrap()取得转换后的值。

从实现思路看,下面,该修改主进程了。主进程的修改比较轻松,只需在Command::new之后添加.arg即可:

use std::process::Command;

fn main() {
    let mut p = Command::new("../sub/target/debug/sub.exe").arg("5").spawn().unwrap();
    p.wait().unwrap();
}

从实现思路看,若是多个参数,能够采用args函数,将参数合同时成一个数组传入就能够了。

进程间通信

Rust当然也能够借助管道进行进程间通信。

落到代码里,修改子进程代码,将函数的参数设置为接收数据的条数,每接收到一条数据就原样得到,直到达到设定的条数后退出:

use std::io;
use std::env::args;

fn main() {
    println!("Hello, world!");
    let mut args = args();
    let count = args.nth(1).unwrap().parse::<u64>().unwrap();
    let mut index = 0;
    let stdin = io::stdin(); // 打开标准输入
    while index < count {
        let mut s = String::new();
        stdin.read_line(&mut s).unwrap(); // 读取标准输入中的一行
        println!("{}", s.trim()); // trim掉最后的n

        index += 1;
    }
    println!("Bye, world!");
}

从实现思路看,然后在主进程中,循环向子进程的标准输入里写入消息,同时读取标准输出的内容:

use std::process::{Command, Stdio};
use std::io::{BufRead, Write, BufReader};

fn main() {
    // 消息数组
    let msglist = ["msg1", "msg2", "msg3", "msg4", "msg5"];

    // 启动子进程
    let mut p = Command::new("../sub/target/debug/sub.exe")
        .arg(msglist.len().to_string()) // 传递消息的个数
        .stdin(Stdio::piped()) // 将子进程的标准输入重定向到管道
        .stdout(Stdio::piped()) // 将子进程的标准输出重定向到管道
        .spawn()
        .unwrap();
    
    let p_stdin = p.stdin.as_mut().unwrap();
    let mut p_stdout = BufReader::new(p.stdout.as_mut().unwrap());
    let mut line = String::new();

    // 接收Hello world
    p_stdout.read_line(&mut line).unwrap();
    println!("{}", line);
    
    // 循化发送消息
    for msg in msglist.iter() {
        // 发送消息
        println!("write to stdid:{}", msg);
        p_stdin.write(msg.as_bytes()).unwrap();
        p_stdin.write("n".as_bytes()).unwrap(); // 发送n,子进程的read_line才会响应

        // 接收消息
        line.clear(); // 需要清空,否则会保留上次的结果
        p_stdout.read_line(&mut line).unwrap();
        println!("read from stdout:{}", line);
    }

    // 接收Bye world
    line.clear();
    p_stdout.read_line(&mut line).unwrap();
    println!("{}", line);

    // 等待子进程结束
    p.wait().unwrap();
}

总结

落到代码里,总的来说,Rust多进程新建适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。

热门栏目