与其像上面的例子那样把一个函数传给 thread::spawn
,把一个闭包当做参数传入更常见。这可以让我们能够捕获要移入新线程的值:
let numbers = vec![1, 2, 3];
thread::spawn(move || {
for n in numbers {
println!("{n}");
}
});
在这里,变量 numbers
的所有权被移到新线程。这里我们必须使用一个 move closure 来转移所有权。否则即使新线程可能比变量生命周期要长,闭包也会通过引用来捕获 numbers
,从而导致编译器错误。
由于一个线程可能会运行到程序执行的最后一刻,所以
spawn
对其参数类型有一个 ‘static约束。换句话说,它只接受那些(可能)永远存在的函数。一个通过引用捕获局部变量的闭包不可能永远存在,因为当局部变量不再存在时,该引用就会失效。
我们可以通过闭包中返回值从而获取到线程中的值,这个返回值可以从 join()
返回的thread::Result
中获得。
let numbers = Vec::from_iter(0..=1000);
let t = thread::spawn(move || {
let len = numbers.len();
let sum = numbers.into_iter().sum::<usize>();
sum / len // 1
});
let average = t.join().unwrap(); // 2
println!("average: {average}");
- 闭包函数返回的值
- 返回的值通过 join() 回送到主线程
而如果 numbers
集合是空的,那么闭包函数中试图 sum / len
就会引发 panic。而 join()
也会返回这个 panic,导致主线程也会因为 unwrap()
而 panic。
📢 THREAD BUILDER
thread::spawn
其实等同于 thread::Builder::new().spawn().unwrap()
。 thread::Builder
可以对新线程进行一些配置:
- 配置新线程的堆栈大小
- 给新线程一个别名 →
thread::current().name()
获取线程名
而获取的线程名,会被用与 panic 信息,开发者可以在监控平台以及一些调试工具中看到
Scoped Threads
⚠️ WARNING
截至2022年3月,Scoped Thread 还不是Rust标准库的一部分。不过它已被提议并作为不稳定的特性来实现,在Rust的nightly版本中可以使用:#![feature(scoped_threads)]
。
如果我们确定一个子线程肯定不会超过某个作用域,那么这个线程就可以安全地借用那些不会永远存在的东西,比如局部变量(只要它们的生命周期不小于当前作用域)。
Rust标准库提供了 thread::scope
来生成这样的 Scoped Thread。我们还是可以生成新线程,而新线程不能超出我们传递给该函数的闭包的范围,从而使安全借用局部变量成为可能。
来个例子说明一下:
let numbers = vec![1, 2, 3];
thread::scope(|s| { // 1
s.spawn(|| { // 2
println!("length: {}", numbers.len());
});
s.spawn(|| { // 2
for n in &numbers {
println!("{n}");
}
});
}); // 3
TODO!!!
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。