Rust Lang Book Ch.16 Concurrency

使用线程来并行化任务一方面可以加快速度,避免因为IO等待耗费太久时间,另外一方面,也带来了资源竞争,死锁和潜在的难以复现的种种bug。不同的编程语言采取不同策略来生成管理调度线程,如果用户在编程语言中申请一个线程,就通过系统接口获取系统的一个线程,那么就称之为1:1模型。编程语言提供的线程被称为green thread,每M个green threads对应N个系统线程的模型就被称之为M:N模型。为了保证Rust在每个binary上附带的runtime code,即额外需要的用于运行的代码最少,Rust只提供了1:1线程模型。

thread::spawn

Rust使用thread::spawn来创建简单的线程JoinHandle,并且可以用JoinHandle::join()来等待所有的线程返回

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

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }

    handle.join().unwrap();
}

  

move使得线程能访问另一个线程中的数据

线程可以使用move来获得另一个线程中拥有的变量(captured variable)的ownership,当然,在move之后,这个变量就不能在原来的线程中使用了。

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

  

如果尝试继续用,比如试着drop,就会报错:

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });

    drop(v); // oh no!
           ^ value used here after move
    handle.join().unwrap();
}

  

channel

channel可以认为有两半:transmitter发送端和receiver接收端。无论是接受还是发送端drop掉了,都可以认为是channel已经关闭了。

 mpsc::channel中的mpsc的包意指multiple producer single consumer,即多个发送者,一个接收者。

发送端可以使用send方法发送数据,接收端可以使用recv或者try_recv来接收数据。对于send方法来说,接收端已经drop掉了会导致返回错误。recv会阻塞直到接收到数据,同样的,如果发送端已经关闭了就会报错。try_recv则不会阻塞。

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

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

  

此外,send会导致ownership的转移,所以send的参数是不能继续用了。

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

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hi");
             --- move occurs because `val` has type `std::string::String`, which does not implement the `Copy` trait
        tx.send(val).unwrap();
                   --- value moved here
        println!("val is {}", val);
                                ^^^ value borrowed here after move
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

 

Shared-State Concurrency

Mutex<T>+ Arc<T>

Mutex本身是个Smart Pointer,Mutex::lock返回一个LockResult结构,里面的Smart Pointer结构MutexGuard能够获取指向数据本身的引用,而Drop会自动释放锁。注意在传给不同线程的时候,可以结合Arc(Atomic Reference Counting)一起使用以传递Mutex本身,因为多个线程必须共享同个Mutex对象。此外,Rc不是线程安全的在这里不能用。Mutex与Arc的组合,正如RefCell和Rc的组合,前者提供了interior mutability,后者则分发引用。

use std::sync::Mutex;
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
let counter = Arc::clone(&counter);
//这里要给每个
let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); }

  

Extensible Concurrency with the Sync and Send traits

Rust语言自身的并发功能很少,大部分并发功能都是在standard library而不是core中实现的。程序员可以通过std::marker中的两个traits Sync和Send来自定义并发功能。Sync和Send都是unsafe的。

Send特性说明实现了该特性的类型是可以在线程之间交换ownership的。Sync特性则说明它的引用可以安全地发送给其他线程,即可以被多个线程所引用。

 

posted @ 2020-10-27 21:03  雪溯  阅读(112)  评论(0编辑  收藏  举报