Struct crossbeam::Scope
[−]
[src]
pub struct Scope<'a> { /* fields omitted */ }
Methods
impl<'a> Scope<'a>
[src]
fn defer<F>(&self, f: F) where
F: FnOnce() + 'a,
F: FnOnce() + 'a,
Schedule code to be executed when exiting the scope.
This is akin to having a destructor on the stack, except that it is guaranteed to be run.
fn spawn<F, T>(&self, f: F) -> ScopedJoinHandle<T> where
F: FnOnce() -> T + Send + 'a,
T: Send + 'a,
F: FnOnce() -> T + Send + 'a,
T: Send + 'a,
Create a scoped thread.
spawn
is similar to the spawn
function in Rust's standard library. The
difference is that this thread is scoped, meaning that it's guaranteed to terminate
before the current stack frame goes away, allowing you to reference the parent stack frame
directly. This is ensured by having the parent thread join on the child thread before the
scope exits.
Examples
A basic scoped thread:
crossbeam::scope(|scope| { scope.spawn(|| { println!("Hello from a scoped thread!"); }); });
When writing concurrent Rust programs, you'll sometimes see a pattern like this, using
std::thread::spawn
:
let array = [1, 2, 3]; let mut guards = vec![]; for i in &array { let guard = std::thread::spawn(move || { println!("element: {}", i); }); guards.push(guard); } for guard in guards { guard.join().unwrap(); }
The basic pattern is:
- Iterate over some collection.
- Spin up a thread to operate on each part of the collection.
- Join all the threads.
However, this code actually gives an error:
error: `array` does not live long enough
for i in &array {
^~~~~
in expansion of for loop expansion
note: expansion site
note: reference must be valid for the static lifetime...
note: ...but borrowed value is only valid for the block suffix following statement 0 at ...
let array = [1, 2, 3];
let mut guards = vec![];
for i in &array {
let guard = std::thread::spawn(move || {
println!("element: {}", i);
...
error: aborting due to previous error
Because std::thread::spawn
doesn't know about this scope, it requires a
'static
lifetime. One way of giving it a proper lifetime is to use an Arc
:
use std::sync::Arc; let array = Arc::new([1, 2, 3]); let mut guards = vec![]; for i in (0..array.len()) { let a = array.clone(); let guard = std::thread::spawn(move || { println!("element: {}", a[i]); }); guards.push(guard); } for guard in guards { guard.join().unwrap(); }
But this introduces unnecessary allocation, as Arc<T>
puts its data on the heap, and we
also end up dealing with reference counts. We know that we're joining the threads before
our function returns, so just taking a reference should be safe. Rust can't know that,
though.
Enter scoped threads. Here's our original example, using spawn
from crossbeam rather
than from std::thread
:
let array = [1, 2, 3]; crossbeam::scope(|scope| { for i in &array { scope.spawn(move || { println!("element: {}", i); }); } });
Much more straightforward.