参考章节《Rust 程序设计语言》第16.1章 使用线程同时运行代码
线程
就是程序内部
,可以同时运行的独立部分。平常我们说的多线程编程
,就是指编写内部有多个任务同时运行的程序
使用 spawn
创建新线程
为了创建一个新线程,需要调用 thread::spawn
函数并传递一个闭包
,并在其中包含希望在新线程运行的代码
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
use std::thread;
use std::time::Duration;
fn main() {
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));
}
}
|
运行这个程序
1
2
3
4
5
6
7
8
9
|
hi number 1 from the main thread!
hi number 1 from the spawned thread!
hi number 2 from the main thread!
hi number 2 from the spawned thread!
hi number 3 from the main thread!
hi number 3 from the spawned thread!
hi number 4 from the main thread!
hi number 4 from the spawned thread!
hi number 5 from the spawned thread!
|
你会发现程序只打印到 5
就结束了,那是因为当主线程
结束时,它的子线程也会结束,而不管其是否执行完毕
。
使用 join
等待所有线程结束
通过将 thread::spawn
方法的返回值
保存在变量
中,然后对其调用 join
方法,当对其调用 join
方法时,它会等待其线程结束
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
use std::thread;
use std::time::Duration;
fn main() {
// 将 thread::spawn 的返回值保存在变量 handle 中,thread::spawn 的返回值类型是 JoinHandle。JoinHandle 是一个拥有所有权的值,当对其调用 join 方法时,它会等待其线程结束。
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
//handle.join().unwrap(); // 如果把下面的join提到这里,则会先等待子线程运行完毕后,再执行下面的for循环
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap(); // 就算主线程运行完毕后,在这里也会等待子线程运行完毕,而不是直接退出
}
|
线程与 move 闭包
这段代码并不能编译,为什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
use std::thread;
fn main() {
let v = vec![1, 2, 3];
let handle = thread::spawn(|| {
println!("Here's a vector: {:?}", v);
});
// drop(v) // 想象一下,如果这里v被释放了
handle.join().unwrap();
}
|
答:由于闭包会捕获环境
,Rust 会推断
如何捕获 v
,因为 println!
只需要 v
的引用,所以闭包尝试借用 v
。
但这里的问题是Rust 并不知道线程会在什么时候执行
,如果在线程执行之前
,v 被释放了
,那么程序肯定就会出错,所以Rust不允许你这么做
线程的执行是根据CPU的调度
来的,在单核CPU
上,线程会去和其他线程争抢CPU执行权
谁抢到就执行谁,没抢到的则转入后台等待下一次争夺CPU执行权
而在多核CPU
上,线程就可以同时运行
,但就算同时运行,v
也有可能被其他线程释放掉
正是由于存在这种不确定因素
,所以Rust出于安全考虑
不允许你编译这段代码
所以说这么多怎么解决这个问题呢?
很简单,我们看如下代码
1
2
3
4
5
6
7
8
9
10
11
|
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();
}
|
通过在闭包之前增加 move
关键字,我们强制闭包获取其使用的值的所有权
,而不是任由 Rust 推断它应该借用值。
- 可以通过
thread::spawn
并传递一个闭包
来开启一个线程
join
方法可以等待其线程运行结束
,需要特别注意的是join
是等待
线程结束,而不是join
时才启动
线程。