线程

在开始编写多线程代码之前,让我们先退一步,谈谈线程是什么,以及为什么我们可能想要使用它们。

什么是线程?

线程是由操作系统管理的执行上下文。每个线程都有自己的栈、指令指针和程序计数器。

单个进程可以管理多个线程。这些线程共享相同的内存空间,这意味着它们可以访问相同的数据。

线程是一个逻辑构造。归根结底,你只能在一个CPU核心(物理执行单元)上一次运行一套指令。由于线程数量可能远远超过CPU核心的数量,操作系统的调度器负责决定在任何给定时间运行哪个线程,通过对它们分配CPU时间来最大化吞吐量和响应速度。

main

当Rust程序启动时,它在一个单一的线程上运行,即主线程。这个线程由操作系统创建,负责运行main函数。

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

fn main() {
    loop {
        thread::sleep(Duration::from_secs(2));
        println!("主线程问候!");
    }
}

std::thread

Rust的标准库提供了一个模块std::thread,允许你创建和管理线程。

spawn

你可以使用std::thread::spawn来创建新线程并在其上执行代码。

例如:

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

fn main() {
    let handle = thread::spawn(|| {
        loop {
            thread::sleep(Duration::from_secs(1));
            println!("来自线程的问候!");
        }
    });
    
    loop {
        thread::sleep(Duration::from_secs(2));
        println!("主线程问候!");
    }
}

如果在Rust Playground上执行这个程序,你会发现主线程和生成的线程并发运行。每个线程独立地进行。

进程终止

当主线程完成时,整个进程将退出。生成的线程将继续运行,直到它完成或主线程完成。

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

fn main() {
    let handle = thread::spawn(|| {
        loop {
            thread::sleep(Duration::from_secs(1));
            println!("来自线程的问候!");
        }
    });

    thread::sleep(Duration::from_secs(5));
}

在上面的例子中,你期望看到大约五次“来自线程的问候!”的消息打印出来。然后,当sleep调用返回时,主线程将结束,生成的线程也会因为整个进程退出而被终止。

join

你也可以通过调用spawn返回的JoinHandle上的join方法来等待生成的线程完成。

use std::thread;
fn main() {
    let handle = thread::spawn(|| {
        println!("来自线程的问候!");
    });

    handle.join().unwrap();
}

在这个例子中,主线程将在退出之前等待生成的线程完成。这引入了一种线程间的同步形式:你保证会在程序退出前看到“来自线程的问候!”的消息被打印,因为主线程不会退出,直到生成的线程完成。