Skip to content

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 上阅读它的源码,看看它是怎么实现的。

Released under the MIT License