Concurrency & Asynchronous Model
Learn Rust's concurrency, asynchronous model, and multi-threading safety, contrasting with JavaScript's event loop mechanism
Concurrency & Asynchronous Model
📖 Learning Objectives
Understand Rust's concurrency model, including multi-threading, asynchronous programming, and thread safety mechanisms, contrasting with JavaScript's single-threaded event loop model.
🎯 Execution Model Comparison
JavaScript's Event Loop
JavaScript uses a single-threaded event loop model:
Although JavaScript is single-threaded, multi-threaded concurrency can be achieved through Web Workers, which offload time-consuming computation tasks to background threads, preventing the main thread from being blocked. This is similar to Rust's multi-threading, but Web Workers cannot directly share memory and require communication via message passing.
Rust's Multi-threading Model
Rust supports true multi-threaded parallel execution:
Execution Model Differences
- Single-threaded vs Multi-threaded: JavaScript single-threaded event loop, Rust supports multi-threaded parallelism
- Concurrency vs Parallelism: JavaScript concurrency not parallelism, Rust can truly parallelize
- Memory Safety: JavaScript runtime checks, Rust compile-time guarantees thread safety
- Performance: JavaScript limited by single-thread, Rust can fully utilize multi-core
🔒 Thread Safety & Ownership
Shared State Management
⚡ Asynchronous Programming
Rust's Asynchronous Programming
Asynchronous vs Multi-threading Comparison
🎯 Concurrency Patterns Comparison
JavaScript's Concurrency Patterns
Rust's Concurrency Patterns
🎯 Exercises
Exercise 1: Thread-Safe Counter
Create a thread-safe counter that supports multiple threads incrementing the count simultaneously:
View Answer
use std::sync::{Arc, Mutex};use std::thread;struct Counter {count: Mutex<i32>,}impl Counter {fn new() -> Self {Counter {count: Mutex::new(0),}}fn increment(&self) {let mut count = self.count.lock().unwrap();*count += 1;}fn get_count(&self) -> i32 {*self.count.lock().unwrap()}}fn main() {let counter = Arc::new(Counter::new());let mut handles = vec![];for _ in 0..10 {let counter = Arc::clone(&counter);let handle = thread::spawn(move || {for _ in 0..100 {counter.increment();}});handles.push(handle);}for handle in handles {handle.join().unwrap();}println!("Final count: {}", counter.get_count()); // Should be 1000}
Exercise 2: Asynchronous Task Processing
Create an asynchronous function that concurrently processes multiple tasks and collects the results:
View Answer
use tokio;use std::time::Duration;async fn process_task(id: i32) -> String {tokio::time::sleep(Duration::from_millis(100)).await;format!("Task {} complete", id)}async fn process_all_tasks() -> Vec<String> {let mut handles = vec![];for i in 0..10 {let handle = tokio::spawn(async move {process_task(i).await});handles.push(handle);}let mut results = vec![];for handle in handles {let result = handle.await.unwrap();results.push(result);}results}#[tokio::main]async fn main() {let results = process_all_tasks().await;println!("All tasks complete: {:?}", results);}
Exercise 3: Producer-Consumer Pattern
Implement a producer-consumer pattern using channels for inter-thread communication:
View Answer
use std::sync::mpsc;use std::thread;use std::time::Duration;fn producer_consumer() {let (tx, rx) = mpsc::channel();// Producerlet producer = thread::spawn(move || {for i in 0..10 {tx.send(format!("Product {}", i)).unwrap();thread::sleep(Duration::from_millis(100));}});// Consumerlet consumer = thread::spawn(move || {for received in rx {println!("Consumed: {}", received);}});producer.join().unwrap();consumer.join().unwrap();}fn main() {producer_consumer();}
📝 Summary
In this chapter, we learned about Rust's concurrency and asynchronous programming:
- Execution Model: Rust supports true multi-threaded parallelism, JavaScript is single-threaded event loop
- Thread Safety: Rust guarantees thread safety at compile time through the ownership system
- Asynchronous Programming: Rust's async/await syntax is similar to JavaScript
- Concurrency Patterns: Safely share state using types like Arc, Mutex, RwLock
- Performance Choice: Multi-threading for CPU-bound, asynchronous for I/O-bound
Key Takeaways
- Rust's ownership system ensures thread safety
- Asynchronous programming provides high-performance I/O handling
- Choosing the right concurrency model is important
- Compile-time checks prevent runtime errors
Next Steps
In the next chapter, we will learn about Rust's type system and traits, and how to build reusable abstractions.
Continue Learning: Type System & Traits