Skip to content

Commit c6a9f67

Browse files
committed
Rust (1)
1 parent 36573f4 commit c6a9f67

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
title: No magic behind Rust (1) - Arc and locks
3+
layout: post
4+
---
5+
6+
In Rust, `Arc` (Atomic Reference Counted) smart pointer enables shared ownership across threads, but **it does not inherently eliminate the need for locks** when **mutable access** to shared data is required. Here's a breakdown:
7+
8+
### 1. **When `Arc` Alone Suffices (No Locks Needed)**
9+
- If the data inside `Arc` is **immutable** (e.g., `Arc<ReadOnlyData>`), no locks are needed. Multiple threads can safely read the data concurrently.
10+
- Example:
11+
```rust
12+
use std::sync::Arc;
13+
use std::thread;
14+
15+
let data = Arc::new(42); // Immutable data
16+
let handles: Vec<_> = (0..10).map(|_| {
17+
let data = Arc::clone(&data);
18+
thread::spawn(move || {
19+
println!("Data: {}", *data); // Safe concurrent reads
20+
})
21+
}).collect();
22+
23+
for handle in handles { handle.join().unwrap(); }
24+
```
25+
26+
### 2. **When You Still Need Locks**
27+
- If the data inside `Arc` needs **mutable access** across threads, you must pair `Arc` with a synchronization primitive like `Mutex` or `RwLock` (e.g., `Arc<Mutex<T>>`).
28+
- Example (with `Mutex`):
29+
```rust
30+
use std::sync::{Arc, Mutex};
31+
use std::thread;
32+
33+
let counter = Arc::new(Mutex::new(0));
34+
let handles: Vec<_> = (0..10).map(|_| {
35+
let counter = Arc::clone(&counter);
36+
thread::spawn(move || {
37+
let mut num = counter.lock().unwrap();
38+
*num += 1; // Safe mutable access
39+
})
40+
}).collect();
41+
42+
for handle in handles { handle.join().unwrap(); }
43+
println!("Result: {}", *counter.lock().unwrap());
44+
```
45+
46+
### 3. **Alternatives to Locks**
47+
- **Atomic Types**: Use `std::sync::atomic` types (e.g., `AtomicUsize`) for simple, lock-free operations.
48+
```rust
49+
use std::sync::atomic::{AtomicUsize, Ordering};
50+
use std::sync::Arc;
51+
use std::thread;
52+
53+
let counter = Arc::new(AtomicUsize::new(0));
54+
let handles: Vec<_> = (0..10).map(|_| {
55+
let counter = Arc::clone(&counter);
56+
thread::spawn(move || {
57+
counter.fetch_add(1, Ordering::SeqCst);
58+
})
59+
}).collect();
60+
61+
for handle in handles { handle.join().unwrap(); }
62+
println!("Result: {}", counter.load(Ordering::SeqCst));
63+
```
64+
- **Channels**: Use message passing (e.g., `std::sync::mpsc`) to transfer ownership of data between threads instead of sharing it.
65+
```rust
66+
use std::sync::mpsc;
67+
use std::thread;
68+
69+
let (tx, rx) = mpsc::channel();
70+
for _ in 0..10 {
71+
let tx = tx.clone();
72+
thread::spawn(move || {
73+
tx.send(1).unwrap();
74+
});
75+
}
76+
drop(tx); // Close sender
77+
println!("Result: {}", rx.iter().sum::<i32>());
78+
```
79+
80+
### 4. **When to Avoid Locks Entirely**
81+
- **Thread-Local Storage**: Use `thread_local!` for data that doesnt need to be shared.
82+
- **Immutable Data**: Design your data to be immutable where possible.
83+
84+
### Key Takeaway
85+
`Arc` manages **ownership** across threads, but **not synchronization**. To eliminate locks, avoid shared mutable state (use message passing, atomics, or immutable data). If you must mutate shared data, use `Mutex` or `RwLock` with `Arc`.
86+
87+
#### Note
88+
Some contents are generated by Deepseek.

0 commit comments

Comments
 (0)