- 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>
implementsSync
whenT: Send
Arc<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 impl
requires careful safety guarantees PhantomData
often 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:
Send
enables ownership transfer between threadsSync
enables shared references (&T) across threads- Most Rust types are
Send/Sync
by default - Composition matters - compound types inherit
Send/Sync
from components - Thread-safe patterns combine
Arc
(Sync) withMutex/RwLock
(Send)
THE END