Rust的并发编程(一)多进程并发

文章目录

  • 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::().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::().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语言学习笔记,rust)