- Published on
Send与Sync:代码示例
1. Basic Send Example: Moving Data Between Threads
use std::thread;
fn main() {
let data = vec![1, 2, 3]; // Vec<i32> is Send + Sync
let handle = thread::spawn(move || {
println!("Data in thread: {:?}", data); // Ownership moved here
});
handle.join().unwrap();
}
Key Point: Vec<i32> implements Send, so its ownership can be transferred across threads.
2. Sync Example: Shared Immutable Data with Arc
use std::sync::Arc;
use std::thread;
fn main() {
let shared_data = Arc::new(42); // Arc<T> requires T: Send + Sync
let handles: Vec<_> = (0..3).map(|_| {
let data = Arc::clone(&shared_data);
thread::spawn(move || {
println!("Thread sees: {}", data); // Shared immutable reference
})
}).collect();
for h in handles {
h.join().unwrap();
}
}
Key Point: Arc<T> allows shared read-only access across threads (Sync behavior).
3. Thread-Safe Mutability with Mutex (Send + Sync)
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0)); // Mutex<T> requires T: Send
let handles: Vec<_> = (0..10).map(|_| {
let counter = Arc::clone(&counter);
thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1; // Safe mutable access
})
}).collect();
for h in handles {
h.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
Key Points:
Mutex<T>implementsSyncwhenT: SendArc<Mutex<T>>is a classic thread-safe interior mutability pattern
4. Non-Send Type Example (Rc)
use std::rc::Rc;
use std::thread;
fn main() {
let data = Rc::new(42);
// This would fail to compile:
// thread::spawn(move || {
// println!("{}", data); // ERROR: Rc<i32> cannot be sent between threads
// });
}
Fix: Replace Rc with Arc to make it Send.
5. Custom Send + Sync Implementation (Advanced)
use std::marker::PhantomData;
struct MyThreadSafePtr<T: Send>(*const T);
// SAFETY: We guarantee pointer access is synchronized externally
unsafe impl<T: Send> Send for MyThreadSafePtr<T> {}
unsafe impl<T: Send> Sync for MyThreadSafePtr<T> {}
fn main() {
let val = 10;
let ptr = MyThreadSafePtr(&val as *const i32);
thread::spawn(move || {
// Can move to thread because we implemented Send
println!("Pointer in thread: {:p}", ptr.0);
}).join().unwrap();
}
Key Points:
- Manual
unsafe implrequires careful safety guarantees PhantomDataoften used for generic types
6. Conditional Send/Sync with Generic Types
use std::marker::PhantomData;
struct Container<T> {
data: T,
_marker: PhantomData<*const ()>, // Affects auto-traits
}
// Only Send when T is Send
unsafe impl<T: Send> Send for Container<T> {}
// Only Sync when T is Sync
unsafe impl<T: Sync> Sync for Container<T> {}
fn main() {
let container = Container {
data: 42,
_marker: PhantomData,
};
thread::spawn(move || {
println!("{}", container.data); // Works because i32 is Send
}).join().unwrap();
}
7. Channel Communication (Send in Action)
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let data = vec![1, 2, 3];
tx.send(data).unwrap(); // Moving ownership through channel
});
println!("Received: {:?}", rx.recv().unwrap());
}
Key Point: Channels require their contents to implement Send.
8. Parallel Processing with Rayon (Sync Showcase)
use rayon::prelude::*;
fn main() {
let data = vec![1, 2, 3, 4, 5];
let sum: i32 = data.par_iter() // Requires &[T] where T: Sync
.map(|x| x * 2)
.sum();
println!("Parallel sum: {}", sum);
}
Key Takeaways:
Sendenables ownership transfer between threadsSyncenables shared references (&T) across threads- Most Rust types are
Send/Syncby default - Composition matters - compound types inherit
Send/Syncfrom components - Thread-safe patterns combine
Arc(Sync) withMutex/RwLock(Send)
THE END