Creating threads in Rust - Performance & Efficiency
When we create threads in Rust, we want to know how the time to start them changes as we create more threads.
We ask: How does the work grow when we add more threads?
Analyze the time complexity of the following code snippet.
use std::thread;
fn main() {
let n = 100usize;
let mut handles = vec![];
for i in 0..n {
let handle = thread::spawn(move || {
println!("Thread {} is running", i);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
This code creates n threads, each printing a message, then waits for all to finish.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Loop creating and starting threads
ntimes. - How many times: Exactly once per thread, so
ntimes.
Each new thread adds a fixed amount of work to start and run.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 thread starts and joins |
| 100 | About 100 thread starts and joins |
| 1000 | About 1000 thread starts and joins |
Pattern observation: The work grows directly with the number of threads.
Time Complexity: O(n)
This means the time to create and wait for threads grows in a straight line as we add more threads.
[X] Wrong: "Creating threads is instant and does not add time as we add more."
[OK] Correct: Each thread takes some time to start and finish, so more threads mean more total time.
Understanding how thread creation time grows helps you write programs that use threads wisely and explain your choices clearly.
"What if we reused a fixed number of threads instead of creating new ones each time? How would the time complexity change?"
Practice
thread::spawn do in Rust?Solution
Step 1: Understand the purpose of
This function is used to start a new thread that runs code independently from the main thread.thread::spawnStep 2: Compare options with the function's role
Only "It creates a new thread to run code concurrently." correctly describes creating a new thread. Other options describe unrelated actions.Final Answer:
It creates a new thread to run code concurrently. -> Option CQuick Check:
thread::spawncreates threads [OK]
- Confusing spawn with pausing or stopping threads
- Thinking spawn merges threads
- Assuming spawn waits for thread completion
Solution
Step 1: Recall the correct module and function
The correct function to create a thread isthread::spawnfrom thestd::threadmodule.Step 2: Check syntax correctness
let handle = thread::spawn(|| { println!("Hello"); }); uses the correct syntax:thread::spawn(|| { ... }). Other options use wrong module names or functions.Final Answer:
let handle = thread::spawn(|| { println!("Hello"); }); -> Option AQuick Check:
Correct syntax usesthread::spawn[OK]
- Swapping module and function names
- Missing double colons (::) in path
- Using non-existent functions like create()
use std::thread;
fn main() {
let handle = thread::spawn(|| {
println!("Inside thread");
});
println!("Outside thread");
handle.join().unwrap();
}Solution
Step 1: Understand thread execution order
The main thread prints "Outside thread" immediately. The spawned thread runs concurrently and prints "Inside thread".Step 2: Consider join() effect
Thejoin()waits for the spawned thread to finish, but does not change the order of prints already done.Final Answer:
Outside thread Inside thread -> Option BQuick Check:
Main prints first, spawned thread prints after [OK]
- Assuming spawned thread always prints first
- Ignoring that join() waits but doesn't reorder prints
- Thinking only one print appears
use std::thread;
fn main() {
let handle = thread::spawn(|| {
println!("Hello from thread");
});
handle.join();
}Solution
Step 1: Check join() usage
Thejoin()method returns a Result that should be handled, usually by callingunwrap()to catch panics.Step 2: Verify other statements
The other options are incorrect: println! macro works inside threads, thread::spawn accepts closures, and join() is called after spawn.Final Answer:
Missing call to unwrap() on join() result. -> Option DQuick Check:
Always unwrap join() result to handle errors [OK]
- Ignoring join()'s Result return
- Thinking closures can't be used in spawn
- Calling join() before spawn
use std::thread;
fn main() {
for i in 1..=3 {
thread::spawn(|| {
println!("Thread number: {}", i);
});
}
}Solution
Step 1: Understand closure capturing in threads
The variableichanges each loop iteration. Withoutmove, the closure capturesiby reference, causing data races.Step 2: Use
Addingmoveto captureiby valuemovemoves the current value ofiinto the closure, making each thread print its own number safely.Final Answer:
Move i into closure:thread::spawn(move || { println!("Thread number: {}", i); });-> Option AQuick Check:
Use move closures to capture loop variables safely [OK]
- Not using move causes all threads to print last i value
- Trying to declare i as static (not allowed here)
- Assuming closure captures by value without move
Ready to prove it?
Pass the test and this pattern moves unknown
