Skip to content

Small fixes to concurrency material #2737

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions src/concurrency/shared-state/arc.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,40 @@ minutes: 5

# `Arc`

[`Arc<T>`][1] allows shared read-only access via `Arc::clone`:
[`Arc<T>`][1] allows shared, read-only ownership via `Arc::clone`:

```rust,editable
use std::sync::Arc;
use std::thread;

/// A struct that prints which thread drops it.
#[derive(Debug)]
struct WhereDropped(Vec<i32>);

impl Drop for WhereDropped {
fn drop(&mut self) {
println!("Dropped by {:?}", thread::current().id())
}
}

fn main() {
let v = Arc::new(vec![10, 20, 30]);
let v = Arc::new(WhereDropped(vec![10, 20, 30]));
let mut handles = Vec::new();
for _ in 0..5 {
for i in 0..5 {
let v = Arc::clone(&v);
handles.push(thread::spawn(move || {
// Sleep for 0-500ms.
std::thread::sleep(std::time::Duration::from_millis(500 - i * 100));
let thread_id = thread::current().id();
println!("{thread_id:?}: {v:?}");
}));
}

// Now only the spawned threads will hold clones of `v`.
drop(v);

// When the last spawned thread finishes, it will drop `v`'s contents.
handles.into_iter().for_each(|h| h.join().unwrap());
println!("v: {v:?}");
}
```

Expand Down
38 changes: 18 additions & 20 deletions src/concurrency/shared-state/example.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ use std::thread;

fn main() {
let v = vec![10, 20, 30];
let handle = thread::spawn(|| {
v.push(10);
});
v.push(1000);
let mut handles = Vec::new();
for i in 0..5 {
handles.push(thread::spawn(|| {
v.push(10 * i);
println!("v: {v:?}");
}));
}

handle.join().unwrap();
println!("v: {v:?}");
handles.into_iter().for_each(|h| h.join().unwrap());
}
```

Expand All @@ -32,21 +34,17 @@ use std::thread;

fn main() {
let v = Arc::new(Mutex::new(vec![10, 20, 30]));

let v2 = Arc::clone(&v);
let handle = thread::spawn(move || {
let mut v2 = v2.lock().unwrap();
v2.push(10);
});

{
let mut v = v.lock().unwrap();
v.push(1000);
let mut handles = Vec::new();
for i in 0..5 {
let v = Arc::clone(&v);
handles.push(thread::spawn(move || {
let mut v = v.lock().unwrap();
v.push(10 * i);
println!("v: {v:?}");
}));
}

handle.join().unwrap();

println!("v: {v:?}");
handles.into_iter().for_each(|h| h.join().unwrap());
}
```

Expand All @@ -56,7 +54,7 @@ Notable parts:
orthogonal.
- Wrapping a `Mutex` in an `Arc` is a common pattern to share mutable state
between threads.
- `v: Arc<_>` needs to be cloned as `v2` before it can be moved into another
- `v: Arc<_>` needs to be cloned to make a new reference for each new spawned
thread. Note `move` was added to the lambda signature.
- Blocks are introduced to narrow the scope of the `LockGuard` as much as
possible.
Expand Down
3 changes: 2 additions & 1 deletion src/concurrency/sync-exercises/link-checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,12 @@ fn spawn_crawler_threads(
result_sender: mpsc::Sender<CrawlResult>,
thread_count: u32,
) {
// To multiplex the non-cloneable Receiver, wrap it in Arc<Mutex<_>>.
let command_receiver = Arc::new(Mutex::new(command_receiver));

for _ in 0..thread_count {
let result_sender = result_sender.clone();
let command_receiver = command_receiver.clone();
let command_receiver = Arc::clone(&command_receiver);
thread::spawn(move || {
let client = Client::new();
loop {
Expand Down