use std::thread;
use std::sync::{Arc, Mutex, mpsc};
use std::time::Duration;
// Example 1: Basic thread spawning
fn basic_threads() {
println!("=== Basic Threads ===");
// Spawn a new thread
let handle = thread::spawn(|| {
for i in 1..5 {
println!("Thread: count {}", i);
thread::sleep(Duration::from_millis(100));
}
});
// Main thread continues
for i in 1..3 {
println!("Main: count {}", i);
thread::sleep(Duration::from_millis(100));
}
// Wait for the spawned thread to finish
handle.join().unwrap();
println!("Thread finished\n");
}
// Example 2: Ownership transfer with move
fn ownership_transfer() {
println!("=== Ownership Transfer ===");
let data = vec![1, 2, 3, 4, 5];
// 'move' keyword transfers ownership to the thread
let handle = thread::spawn(move || {
println!("Thread owns data now: {:?}", data);
data.iter().sum::<i32>()
});
// data is no longer accessible here (ownership moved)
// println!("{:?}", data); // This would ERROR!
let sum = handle.join().unwrap();
println!("Sum calculated in thread: {}\n", sum);
}
// Example 3: Message passing with channels
fn message_passing() {
println!("=== Message Passing ===");
// Create a channel: (sender, receiver)
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let messages = vec![
"Hello",
"from",
"the",
"thread",
];
for msg in messages {
tx.send(msg).unwrap();
thread::sleep(Duration::from_millis(200));
}
});
// Receive messages in main thread
for received in rx {
println!("Received: {}", received);
}
println!();
}
// Example 4: Multiple producers, single consumer
fn multiple_producers() {
println!("=== Multiple Producers ===");
let (tx, rx) = mpsc::channel();
// Clone sender for multiple threads
let tx1 = tx.clone();
let tx2 = tx.clone();
thread::spawn(move || {
tx1.send("Message from thread 1").unwrap();
});
thread::spawn(move || {
tx2.send("Message from thread 2").unwrap();
});
// Original sender
thread::spawn(move || {
tx.send("Message from thread 3").unwrap();
});
// Receive all messages
for _ in 0..3 {
println!("{}", rx.recv().unwrap());
}
println!();
}
// Example 5: Shared state with Arc and Mutex
fn shared_state_mutex() {
println!("=== Shared State with Mutex ===");
// Arc = Atomic Reference Counted (shared ownership across threads)
// Mutex = Mutual Exclusion (only one thread can access at a time)
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for i in 0..5 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
// Lock the mutex to access the data
let mut num = counter.lock().unwrap();
*num += 1;
println!("Thread {} incremented counter", i);
}); // Lock is automatically released here
handles.push(handle);
}
// Wait for all threads to finish
for handle in handles {
handle.join().unwrap();
}
println!("Final counter value: {}", *counter.lock().unwrap());
println!();
}
// Example 6: Preventing data races with ownership
fn ownership_prevents_races() {
println!("=== Ownership Prevents Data Races ===");
let mut data = vec![1, 2, 3];
// This would NOT compile - can't share mutable reference across threads:
// let handle = thread::spawn(|| {
// data.push(4); // ERROR: can't capture mutable reference
// });
// data.push(5); // ERROR: data already borrowed
// Instead, use Arc<Mutex<T>> for shared mutable state
let data = Arc::new(Mutex::new(vec![1, 2, 3]));
let data_clone = Arc::clone(&data);
let handle = thread::spawn(move || {
let mut d = data_clone.lock().unwrap();
d.push(4);
});
handle.join().unwrap();
println!("Data after thread: {:?}", *data.lock().unwrap());
println!();
}
// Example 7: Deadlock prevention with lock ordering
fn deadlock_example() {
println!("=== Deadlock Prevention ===");
let resource1 = Arc::new(Mutex::new(0));
let resource2 = Arc::new(Mutex::new(0));
// Good practice: always lock in the same order
let r1 = Arc::clone(&resource1);
let r2 = Arc::clone(&resource2);
let handle1 = thread::spawn(move || {
let _lock1 = r1.lock().unwrap();
println!("Thread 1: locked resource 1");
thread::sleep(Duration::from_millis(50));
let _lock2 = r2.lock().unwrap();
println!("Thread 1: locked resource 2");
});
let r1 = Arc::clone(&resource1);
let r2 = Arc::clone(&resource2);
let handle2 = thread::spawn(move || {
// Lock in SAME order as thread 1 (prevents deadlock)
let _lock1 = r1.lock().unwrap();
println!("Thread 2: locked resource 1");
thread::sleep(Duration::from_millis(50));
let _lock2 = r2.lock().unwrap();
println!("Thread 2: locked resource 2");
});
handle1.join().unwrap();
handle2.join().unwrap();
println!();
}
// Example 8: Send and Sync traits
fn send_sync_traits() {
println!("=== Send and Sync Traits ===");
// Send: type can transfer ownership between threads
// Sync: type can be referenced from multiple threads
// Most types are Send and Sync
let data = vec![1, 2, 3]; // Vec is Send and Sync
thread::spawn(move || {
println!("Vec is Send, ownership transferred: {:?}", data);
}).join().unwrap();
// Arc allows sharing across threads (Sync)
let shared_data = Arc::new(vec![1, 2, 3]);
let shared_clone = Arc::clone(&shared_data);
thread::spawn(move || {
println!("Arc is Sync, shared reference: {:?}", shared_clone);
}).join().unwrap();
println!("Original still accessible: {:?}", shared_data);
println!();
}
// Example 9: Scoped threads (no need for 'static lifetime)
fn scoped_threads() {
println!("=== Scoped Threads ===");
let mut data = vec![1, 2, 3];
thread::scope(|s| {
// Can borrow non-'static data safely
s.spawn(|| {
println!("Scoped thread can read: {:?}", data);
});
s.spawn(|| {
println!("Another scoped thread: {:?}", data);
});
// All scoped threads complete before scope ends
});
// data is still accessible
data.push(4);
println!("After scoped threads: {:?}", data);
println!();
}
// Example 10: Parallel iteration with rayon
// Note: This example shows the concept, but rayon isn't in std
fn parallel_computation_concept() {
println!("=== Parallel Computation Concept ===");
let numbers: Vec<i32> = (0..100).collect();
// Sequential
let sum: i32 = numbers.iter().map(|x| x * 2).sum();
println!("Sequential sum: {}", sum);
// With rayon (external crate), you could do:
// let sum: i32 = numbers.par_iter().map(|x| x * 2).sum();
// This automatically uses multiple threads!
println!();
}
fn main() {
basic_threads();
ownership_transfer();
message_passing();
multiple_producers();
shared_state_mutex();
ownership_prevents_races();
deadlock_example();
send_sync_traits();
scoped_threads();
parallel_computation_concept();
println!("=== Key Takeaways ===");
println!("1. Ownership prevents data races at compile time");
println!("2. Use channels (mpsc) for message passing");
println!("3. Use Arc<Mutex<T>> for shared mutable state");
println!("4. Send trait: ownership can transfer between threads");
println!("5. Sync trait: safe to reference from multiple threads");
println!("6. Compiler enforces thread safety!");
}