Appearance
31.2 scoped-threadpool
在前面的章节中,我们已经知道,如果要在多线程之间共享变量,必须使用 Arc 这样的保证线程安全的智能指针。然而,Arc 是有运行期开销的(虽然很小)。假如我们有时候需要子线程访问当前调用栈中的局部变量,而且能保证当前函数的生命周期一定大于子线程的生命周期,子线程一定先于当前函数退出,那我们能不能直接在子线程中使用最简单的借用指针&来访问父线程栈上的局部对象呢?
至少标准库中的 spawn 函数是不行的。spawn 的签名是:
rust
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static
注意这里的闭包要满足 f:'static
约束。这意味着闭包中存在不能捕获短生命周期的变量,比如指向当前局部调用栈的指针。这是因为 spawn 函数会将闭包传递给一个新的子线程,这个子线程的生命周期很可能大于当前函数调用生命周期。如果我们希望在子线程中访问当前函数中的局部变量,怎么办呢?可以使用第三方库 scoped_threadpool。我们来看看 scoped_threadpool 是如何使用的:
rust
extern crate scoped_threadpool;
use scoped_threadpool::Pool;
fn main() {
let mut pool = Pool::new(4);
let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7];
pool.scoped(|scope| {
for e in &mut vec {
scope.execute(move || {
*e += 1;
});
}
});
println!("{:?}", vec);
}
在这里,线程内部直接使用了&mut vec 形式访问了父线程“栈”上的变量,而不必使用 Arc。我们可以注意到,scoped 方法的签名是这样的:
rust
fn scoped<'pool, 'scope, F, R>(&'pool mut self, f: F) -> R
where F: FnOnce(&Scope<'pool, 'scope>) -> R
这里,参数闭包的约束条件没有'static 这一项。所以我们上面的调用是可以成功的。scoped_threadpool 库的源码并不复杂,只需一个文件即可,各位读者可以自己去 GitHub 上阅读它的源码,看看它是怎么实现的。