在现代操作系统中,被执行程序的代码在一个进程中运行,操作系统同时管理多个进程。在你的程序中,你也可以有同时运行的独立部分。运行这些独立部分的功能被称为线程。
线程 是进程的一部分,它与主进程一起运行,使用的技术称为 context-switching.因此,我们可以在Rust 中创建一个线程,并在我们的程序中实现并发性。
将你的程序分成多个线程是一个好主意,但它给我们的代码增加了很多复杂性。这是由于线程可以同时运行的事实,所以不能保证不同线程上的代码部分的运行顺序。这可能会导致诸如竞赛条件、 死锁等问题。Rust提供了一些功能来减少线程的负面影响。
![]()
![]()
用spoon创建一个新线程
现在我们知道了线程以及它们的工作原理,现在我们将看看如何在Rust中定义线程。我们可以在Rust中用以下方法创建一个线程 **thread::spawn**函数。我们向这个函数传递一个闭包,其中包含我们想要作为一个线程运行的代码。
下面是一个例子,展示了我们如何在 Rust中创建一个线程以及它们是如何被执行的。
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for _index in 1..10 {
println!("in spawned thread");
thread::sleep(Duration::from_millis(1));
}
});
for _index in 1..5 {
println!("in main thread");
thread::sleep(Duration::from_millis(1));
}
}
调用 thread::sleep强制一个线程在短时间内停止执行,允许另一个线程运行。当我们编译这段代码时,我们得到以下输出。
in main thread
in spawned thread
in main thread
in spawned thread
in main thread
in spawned thread
in main thread
in spawned thread
in spawned thread
你可以看到,线程的执行顺序是无法预测的。但值得注意的是,当主线程 结束时,它也停止了我们创建的产卵线程的执行。"在产卵线程中 "的信息被打印了5次,但循环却进行了10次。这是不可取的,因为它突然关闭了我们的产卵线程,导致了问题。
使用连接处理程序等待所有线程完成
正如我们在前面的例子中所看到的,我们的产卵线程并没有完全执行。另一方面,我们的主线程一停止,它就停止了。我们可以通过在一个变量中存储一个线程的返回值来解决这个问题。 thread::spawn的返回值存储在一个变量中。的返回类型是 thread::spawn的返回类型是 JoinHandle.A JoinHandle是一个自有值,当我们对它调用join方法时,它将等待其线程结束。
为了更清楚地了解情况,请看下面的例子。
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for _index in 1..10 {
println!("in the spawned thread");
thread::sleep(Duration::from_millis(1));
}
});
for _index in 1..5 {
println!("in the main thread");
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap();
}
生成线程 的返回值被存储在一个句柄变量中,然后我们对它调用一个连接方法,这样我们的生成线程就会完全执行,即使我们的主线程已经完成。下面是上述代码的输出。
in the main thread
in the spawned thread
in the main thread
in the spawned thread
in the main thread
in the spawned thread
in the main thread
in the spawned thread
in the spawned thread
in the spawned thread
in the spawned thread
in the spawned thread
in the spawned thread
现在你可以看到在主线程结束后,生成的线程被完全执行。这使得我们可以在Rust中实现并发。
这就是对Rust中线程的介绍。请继续关注即将发布的关于Rust中的并发性的博客。