rust 并发

go 语言面试中,经常会被问到 go 语言的优势,go 的高并发能力便是优势之一。那么,rust 的并发如何呢?

spawn

use std::thread;

fn main() {
    let handler = thread::spawn(|| {
        // thread code
        println!("thread");
    });

    println!("main");
    handler.join().unwrap();
}

我们调用 thread::spawn 函数来创建线程,它接收一个闭包函数作为参数,函数类型实现了 FnOnce,闭包中实现异步执行的代码。FnOnce特性闭包只能被执行一次。

Go语言开启协程也使用了闭包,但它依赖 sync.WaitGroup对象来等待多个协程执行结束,相当于依赖了一个独立的三方,模式上属于1:N 的控制。

而 rust 依赖 spawn 的返回值来等待线程执行结束,它返回 JoinHandle 类型对象,调用它的 join 方法可以阻塞当前线程直到对应的异步线程运行结束。但每次仅仅只能管控一个异步的线程,模式上属于 1:1 的控制。

Unwrap() 属于处理 result 类型的快捷方法。如果 result 是成功的结果,unwrap 也会返回成功的结果。如果是错误的结果,方法会发生 panic。常见的还有 expect() 方法,可以给 panic 指定错误信息。

channel

Go 语言的 channel 通讯的口号:Do not communicate by sharing memory; instead, share memory by communicating,rust 中 channel 也展示了同样的设计理念。

use std::sync::mpsc::channel;
let (sender, receiver) = channel();

rust也可以通过 channel 在线程间通讯, channel 函数会返回 2 个对象,消息的发送者 Sender 和接收者 Receiver。Rust 可以推断出 channel 的具体类型,当然也可以明确类型声明。

Doc下示例代码,对于 std::sync::mpsc 中 mpsc 的全称描述: Multi-producer, single-consumer FIFO queue communication primitives. 多生产者,单消费者。

use std::thread;
use std::sync::mpsc::channel;


fn main() {
    let (sender, receiver) = channel::<String>();

    thread::spawn(move || {
        sender.send("Hello".to_string()).unwrap();
    });

    // Let's see what that answer was
    println!("receive:{:?}", receiver.recv().unwrap());
}

因为有所有权转移的限制,Sender 可以克隆多个副本,并在不同的线程中向 channel 写入,但 Receiver 没有实现 Clone 特性,有且只能存在一个。如果要实现多个线程从同一个通道接收值,就需要使用 Mutex。

下面是官方提供的 channel 示例,这个例子丰富地演示了 channel 通讯的细节。

use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::thread;

static NTHREADS: i32 = 3;

fn main() {
    // Channels have two endpoints: the `Sender` and the `Receiver`,
    // where `T` is the type of the message to be transferred
    // (type annotation is superfluous)
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();
    let mut children = Vec::new();

    for id in 0..NTHREADS {
        // The sender endpoint can be copied
        let thread_tx = tx.clone();

        // Each thread will send its id via the channel
        let child = thread::spawn(move || {
            // The thread takes ownership over `thread_tx`
            // Each thread queues a message in the channel
            thread_tx.send(id).unwrap();

            // Sending is a non-blocking operation, the thread will continue
            // immediately after sending its message
            println!("thread {} finished", id);
        });

        children.push(child);
    }

    // Here, all the messages are collected
    let mut ids = Vec::with_capacity(NTHREADS as usize);
    for _ in 0..NTHREADS {
        // The `recv` method picks a message from the channel
        // `recv` will block the current thread if there are no messages available
        ids.push(rx.recv());
    }
    
    // Wait for the threads to complete any remaining work
    for child in children {
        child.join().expect("oops! the child thread panicked");
    }

    // Show the order in which the messages were sent
    println!("{:?}", ids);
}

Go 的 channel 有带 buffer 和不带 buffer 两种情况,不支持我们向 channel 中无限写入。但 rust 的 channel 居然支持无限写入,如果 receiver 处理不过来,会导致消息在内存中堆积。

我们先从如何使用并发谈起,接下来会列举几个例子来说明 rust 并发编程

你可能感兴趣的:(rust,编程开发,rust)