线程是开发一个高并发程序的基础,也是数据同步这一难点的起源。

由于绿色线程的M:N模型需要一个较大的运行时来管理线程,所以Rust标准库只提供了1:1线程模型(一个操作系统线程对应一个语言线程)的实现。但得益于Rust良好的底层抽象能力,Rust社区中涌现出了许多支持M:N线程模型的第三方包。

使用spawn创建新线程

使用thread::spawn可以创建一个新线程,接收一个闭包作为参数。 一个线程交替打印信息的简单示例如下:

use std::thread;
use std::time::Duration;
 
fn main() {
    thread::spawn(|| {
        for i in 1..10 {
            println!("spawn thread: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    
    for i in 1..5 {
        println!("main thread: {}", i);
        thread::sleep(Duration::from_millis(1));
    }
}

使用join等待所有线程结束

在上面的例子当中,主线程结束后,不管其余线程是否结束,整个程序都会结束。 为了让所有线程结束后再结束整个程序,使用join来等待所有线程。

将spawn保存到一个变量,其join方法可以阻塞当前线程直到它结束。

use std::thread;
use std::time::Duration;
 
fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("spawn thread: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });
    
    for i in 1..5 {
        println!("main thread: {}", i);
        thread::sleep(Duration::from_millis(1));
    }
    handle.join().unwrap();
}

在线程中使用move闭包

使用move可以让一个闭包捕获值的所有权,因为thread::spawn参数是一个闭包,这使得move闭包在线程中非常有用。可以跨线程传递值的所有权。

因为如果spawn线程使用了主线程中的数据,两个线程又是独立运行的,那么闭包获得的是引用的话,无法保证线程执行的时候引用是否有效,因此Rust不会编译通过这种代码。这就需要使用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();
}

tags: 并发