线程
在开始编写多线程代码之前,让我们先退一步,谈谈线程是什么,以及为什么我们可能想要使用它们。
什么是线程?
线程是由操作系统管理的执行上下文。每个线程都有自己的栈、指令指针和程序计数器。
单个进程可以管理多个线程。这些线程共享相同的内存空间,这意味着它们可以访问相同的数据。
线程是一个逻辑构造。归根结底,你只能在一个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(); }
在这个例子中,主线程将在退出之前等待生成的线程完成。这引入了一种线程间的同步形式:你保证会在程序退出前看到“来自线程的问候!”的消息被打印,因为主线程不会退出,直到生成的线程完成。