Channels, Arc and Mutex
Channels, Arc and Mutex
Workers can communicate by sending messages or by coordinating access to shared data. A channel transfers values between participants. Arc keeps shared ownership across threads, and Mutex controls exclusive access to its protected value. These tools have different responsibilities.
What you’ll learn
- Choose messages or synchronized shared data for communication.
- Explain the separate roles of Arc, Mutex and join.
Channels, Arc and Mutex in action
Channel transfers mark 80
Recv obtains 80 before main prints the first line.
Channel transfers mark 80Mutex protects two increments
Each counter worker acquires exclusive access and increments once.
Mutex protects two incrementsJoin all: final count 2
Joining both workers establishes their completion; the final lock then reads two.
Join all: final count 2
Channels, Arc and Mutex explained
A sender’s send transfers a message to the channel. Recv waits for a message or reports that the channel is disconnected. Arc::clone shares ownership of the same mutex. Lock returns a guard; dropping that guard releases the lock. Arc alone does not make arbitrary mutation safe.
Work through the Rust program
Save the complete source as main.rs in the Rust environment. No standard input is needed.
Rust 2024 · complete program
use std::{sync::{mpsc, Arc, Mutex}, thread};
fn main() {
let (tx, rx) = mpsc::channel();
let sender = thread::spawn(move || tx.send(80).expect("receiver exists"));
println!("Message: {}", rx.recv().expect("sender sends"));
sender.join().expect("sender finishes");
let count = Arc::new(Mutex::new(0));
let mut workers = Vec::new();
for _ in 0..2 {
let shared = Arc::clone(&count);
workers.push(thread::spawn(move || { *shared.lock().expect("not poisoned") += 1; }));
}
for worker in workers { worker.join().expect("worker finishes"); }
println!("Count: {}", *count.lock().expect("not poisoned"));
}Expected output
Message: 80
Count: 2Follow the execution
Recv obtains 80 before main prints the first line. Each counter worker acquires exclusive access and increments once. Joining both workers establishes their completion; the final lock then reads two. Their internal completion order is unspecified.
Try the complete program in the Rust compiler. For tests, Cargo commands and local files, use the specified local environment.
Try a small variation
Your task: Would Arc without a Mutex make unsynchronized shared mutation safe?
Rust 2024 · complete program
use std::{sync::{mpsc, Arc, Mutex}, thread};
fn main() {
let (tx, rx) = mpsc::channel();
let sender = thread::spawn(move || tx.send(80).expect("receiver exists"));
println!("Message: {}", rx.recv().expect("sender sends"));
sender.join().expect("sender finishes");
let count = Arc::new(Mutex::new(0));
let mut workers = Vec::new();
for _ in 0..2 {
let shared = Arc::clone(&count);
workers.push(thread::spawn(move || { *shared.lock().expect("not poisoned") += 1; }));
}
for worker in workers { worker.join().expect("worker finishes"); }
println!("Count: {}", *count.lock().expect("not poisoned"));
}Worked answer
No. Arc manages shared ownership, while the Mutex provides the access synchronization here. Waiting for workers and protecting mutations solve separate problems.
Mistakes and limits to recognize
Mutex locking can deadlock, and poisoning needs a deliberate recovery decision. Keep locked work short and avoid holding a guard across operations that may wait on another lock. Channels can block or disconnect; they are not automatically bounded queues or durable delivery systems.
API details: official Rust documentation for this boundary.
Check your understanding
Apply the rule
Take the idea into your next program
No. Arc manages shared ownership, while the Mutex provides the access synchronization here. Waiting for workers and protecting mutations solve separate problems. Mutex locking can deadlock, and poisoning needs a deliberate recovery decision.
Further reading: the official Rust Book on channels, arc and mutex.
Reaching the recap and completing the quizzes are separate milestones.