Module crossbeam_utils::thread

source ·
Expand description

Threads that can borrow variables from the stack.

Create a scope when spawned threads need to access variables on the stack:

use crossbeam_utils::thread;

let people = vec![
    "Alice".to_string(),
    "Bob".to_string(),
    "Carol".to_string(),
];

thread::scope(|s| {
    for person in &people {
        s.spawn(move |_| {
            println!("Hello, {}!", person);
        });
    }
}).unwrap();

§Why scoped threads?

Suppose we wanted to re-write the previous example using plain threads:

use std::thread;

let people = vec![
    "Alice".to_string(),
    "Bob".to_string(),
    "Carol".to_string(),
];

let mut threads = Vec::new();

for person in &people {
    threads.push(thread::spawn(move || {
        println!("Hello, {}!", person);
    }));
}

for thread in threads {
    thread.join().unwrap();
}

This doesn’t work because the borrow checker complains about people not living long enough:

error[E0597]: `people` does not live long enough
  --> src/main.rs:12:20
   |
12 |     for person in &people {
   |                    ^^^^^^ borrowed value does not live long enough
...
21 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

The problem here is that spawned threads are not allowed to borrow variables on stack because the compiler cannot prove they will be joined before people is destroyed.

Scoped threads are a mechanism to guarantee to the compiler that spawned threads will be joined before the scope ends.

§How scoped threads work

If a variable is borrowed by a thread, the thread must complete before the variable is destroyed. Threads spawned using std::thread::spawn can only borrow variables with the 'static lifetime because the borrow checker cannot be sure when the thread will complete.

A scope creates a clear boundary between variables outside the scope and threads inside the scope. Whenever a scope spawns a thread, it promises to join the thread before the scope ends. This way we guarantee to the borrow checker that scoped threads only live within the scope and can safely access variables outside it.

§Nesting scoped threads

Sometimes scoped threads need to spawn more threads within the same scope. This is a little tricky because argument s lives inside the invocation of thread::scope() and as such cannot be borrowed by scoped threads:

use crossbeam_utils::thread;

thread::scope(|s| {
    s.spawn(|_| {
        // Not going to compile because we're trying to borrow `s`,
        // which lives *inside* the scope! :(
        s.spawn(|_| println!("nested thread"));
    });
});

Fortunately, there is a solution. Every scoped thread is passed a reference to its scope as an argument, which can be used for spawning nested threads:

use crossbeam_utils::thread;

thread::scope(|s| {
    // Note the `|s|` here.
    s.spawn(|s| {
        // Yay, this works because we're using a fresh argument `s`! :)
        s.spawn(|_| println!("nested thread"));
    });
}).unwrap();

Structs§

Functions§

  • Creates a new scope for spawning threads.