From 8d9f5104cc2f225e645df498764a2463ce9630f9 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Tue, 1 Apr 2025 22:07:12 -0500 Subject: [PATCH] ch16 done --- ch16/extensible-concurrency/Cargo.lock | 7 ++ ch16/extensible-concurrency/Cargo.toml | 6 ++ ch16/extensible-concurrency/src/main.rs | 20 +++++ ch16/message-passing/Cargo.lock | 7 ++ ch16/message-passing/Cargo.toml | 6 ++ ch16/message-passing/src/main.rs | 100 +++++++++++++++++++++++ ch16/shared-state/Cargo.lock | 7 ++ ch16/shared-state/Cargo.toml | 6 ++ ch16/shared-state/src/main.rs | 101 ++++++++++++++++++++++++ ch16/threads/Cargo.lock | 7 ++ ch16/threads/Cargo.toml | 6 ++ ch16/threads/src/main.rs | 38 +++++++++ 12 files changed, 311 insertions(+) create mode 100644 ch16/extensible-concurrency/Cargo.lock create mode 100644 ch16/extensible-concurrency/Cargo.toml create mode 100644 ch16/extensible-concurrency/src/main.rs create mode 100644 ch16/message-passing/Cargo.lock create mode 100644 ch16/message-passing/Cargo.toml create mode 100644 ch16/message-passing/src/main.rs create mode 100644 ch16/shared-state/Cargo.lock create mode 100644 ch16/shared-state/Cargo.toml create mode 100644 ch16/shared-state/src/main.rs create mode 100644 ch16/threads/Cargo.lock create mode 100644 ch16/threads/Cargo.toml create mode 100644 ch16/threads/src/main.rs diff --git a/ch16/extensible-concurrency/Cargo.lock b/ch16/extensible-concurrency/Cargo.lock new file mode 100644 index 0000000..453748d --- /dev/null +++ b/ch16/extensible-concurrency/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "extensible-concurrency" +version = "0.1.0" diff --git a/ch16/extensible-concurrency/Cargo.toml b/ch16/extensible-concurrency/Cargo.toml new file mode 100644 index 0000000..fcde9e3 --- /dev/null +++ b/ch16/extensible-concurrency/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "extensible-concurrency" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/ch16/extensible-concurrency/src/main.rs b/ch16/extensible-concurrency/src/main.rs new file mode 100644 index 0000000..c22ec40 --- /dev/null +++ b/ch16/extensible-concurrency/src/main.rs @@ -0,0 +1,20 @@ +//! # Extensible Concurrency with the `Sync` and `Send` Traits +//! +//! Two concurrency concepts embedded in the language: the std::marker +//! `Sync` and `Send`. + +fn main() { + // Allowing Transference of ownership between threads with `Send` + // + // `Send` marker traits indicates ownership of values of the + // type implementing `Send` can be transferred between threads. + // Almost every Rust type is `Send` besides `Rc`. + + // Allowing Access from Multiple Threads with `Sync` + // + // The `Sync` marker trait indicates it's safe for the type implementing + // `Sync` to be referenced from multiple threads. + // + // Implementing Send and Sync Manually Is Unsafe + // This doesn't exactly explain why, just says look at Chapter 20. +} diff --git a/ch16/message-passing/Cargo.lock b/ch16/message-passing/Cargo.lock new file mode 100644 index 0000000..0a53e8a --- /dev/null +++ b/ch16/message-passing/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "message-passing" +version = "0.1.0" diff --git a/ch16/message-passing/Cargo.toml b/ch16/message-passing/Cargo.toml new file mode 100644 index 0000000..24bd2ee --- /dev/null +++ b/ch16/message-passing/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "message-passing" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/ch16/message-passing/src/main.rs b/ch16/message-passing/src/main.rs new file mode 100644 index 0000000..4c04e41 --- /dev/null +++ b/ch16/message-passing/src/main.rs @@ -0,0 +1,100 @@ +//! One way to ensure safe concurrency is message passing. +//! Threads send data between one another. +//! To accomplish message-sending concurrency, Rust +//! provides an implementation of channels. Channel +//! is generally a way for data to sent from one +//! channel to another. +//! +//! A channel has two halves: a transmitter and a reciever. +//! Channel is *closed* if either the transmitter or +//! reciever is dropped. + +use std::sync::mpsc; +use std::thread; +use std::time::Duration; + +fn main() { + // mpsc stands for multiple producer, single consumer. + // Rust can have multilple sending and only one receiving + // tx for transmitter and rx for receiver. + let (tx, rx) = mpsc::channel(); + + // spawned thread needs to own transmitter to send messages. + thread::spawn(move || { + let val = String::from("hi"); + tx.send(val).unwrap(); + // Can't use after `send` call because the receiving + // thread may modify the value. + // println!("val is {val}"); + }); + + // receiver has two useful methods: `recv` and `try_recv`. + // `recv` will block main thread's execution. + // `try_recv` useful if this thread has other work to do + // while waiting for messages. + let received = rx.recv().unwrap(); + println!("Got: {received}"); + + // Sending multiple values and seeing the reciever waiting + + println!("Sending multiple values and seeing the receiver waiting section"); + + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + let vals = vec![ + String::from("hi"), + String::from("from"), + String::from("the"), + String::from("thread"), + ]; + + for val in vals { + tx.send(val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + + for received in rx { + println!("Got: {received}"); + } + + // Creating multiple producers by cloning the transmitter + + println!("Creating multiple producers by cloning the transmitter"); + + let (tx, rx) = mpsc::channel(); + + let tx1 = tx.clone(); + thread::spawn(move || { + let vals = vec![ + String::from("hi"), + String::from("from"), + String::from("the"), + String::from("thread"), + ]; + + for val in vals { + tx1.send(val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + + thread::spawn(move || { + let vals = vec![ + String::from("more"), + String::from("messages"), + String::from("for"), + String::from("you"), + ]; + + for val in vals { + tx.send(val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + + for received in rx { + println!("Got: {received}"); + } +} diff --git a/ch16/shared-state/Cargo.lock b/ch16/shared-state/Cargo.lock new file mode 100644 index 0000000..0eaa599 --- /dev/null +++ b/ch16/shared-state/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "shared-state" +version = "0.1.0" diff --git a/ch16/shared-state/Cargo.toml b/ch16/shared-state/Cargo.toml new file mode 100644 index 0000000..da297ea --- /dev/null +++ b/ch16/shared-state/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "shared-state" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/ch16/shared-state/src/main.rs b/ch16/shared-state/src/main.rs new file mode 100644 index 0000000..457a82a --- /dev/null +++ b/ch16/shared-state/src/main.rs @@ -0,0 +1,101 @@ +//! # Shared-State Concurrency +//! +//! This is another way to handle concurrency. Sharing data. +//! Consider the slogan: "do not communicate by sharing memory." + +use std::rc::Rc; +use std::sync::{Arc, Mutex}; +use std::thread; + +fn main() { + // Using mutexes to allow access to data from one thread at a time + // + // Mutex is an abbreviation for mutual exclusion. Allows only one + // thread to access some data at any given time. + // To access data in a mutex, thread must first signal it wants + // access by asking to acquire the mutex's lock. The lock is + // a data structure part of the mutex that keeps track of who + // currently has exclusive access to the data. Therefore, + // mutex described as guarding the data. + // + // Mutexes have two rules: + // 1) Must attempt to acquire the lock before using the data + // 2) When done with data that mutex guards, must unlock data + // so other threads can acquire lock. + + let m = Mutex::new(5); + + { + let mut num = m.lock().unwrap(); + *num = 6; + } + + println!("m = {m:?}"); + + // Sharing a Mutex Between multiple threads + + // let counter = Mutex::new(0); + // let mut handles = vec![]; + + // for _ in 0..10 { + // let handle = thread::spawn(move || { + // let mut num = counter.lock().unwrap(); + + // *num += 1; + // }); + // handles.push(handle); + // } + + // for handle in handles { + // handle.join().unwrap(); + // } + + // println!("Result: {}", *counter.lock().unwrap()); + + // Multiple ownership with multiple threads + + // let counter = Rc::new(Mutex::new(0)); + // let mut handles = vec![]; + + // for _ in 0..10 { + // let counter = Rc::clone(&counter); + // let handle = thread::spawn(move || { + // let mut num = counter.lock().unwrap(); + + // *num += 1; + // }); + // handles.push(handle); + // } + + // for handle in handles { + // handle.join().unwrap(); + // } + + // println!("Result: {}", *counter.lock().unwrap()); + + // Atomic reference counting with Arc + // + // Arc is like Rc is safe to use concurrent situations. + // The a stands for atomic meanings it's an atomically reference + // counted type. Thread safety like this comes at an example. + // Arc and Rc have the same API. + + let counter = Arc::new(Mutex::new(0)); + let mut handles = vec![]; + + for _ in 0..10 { + let counter = Arc::clone(&counter); + let handle = thread::spawn(move || { + let mut num = counter.lock().unwrap(); + + *num += 1; + }); + handles.push(handle); + } + + for handle in handles { + handle.join().unwrap(); + } + + println!("Result: {}", *counter.lock().unwrap()); +} diff --git a/ch16/threads/Cargo.lock b/ch16/threads/Cargo.lock new file mode 100644 index 0000000..e412e95 --- /dev/null +++ b/ch16/threads/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "threads" +version = "0.1.0" diff --git a/ch16/threads/Cargo.toml b/ch16/threads/Cargo.toml new file mode 100644 index 0000000..bd4edf7 --- /dev/null +++ b/ch16/threads/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "threads" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/ch16/threads/src/main.rs b/ch16/threads/src/main.rs new file mode 100644 index 0000000..113b20a --- /dev/null +++ b/ch16/threads/src/main.rs @@ -0,0 +1,38 @@ +use std::thread; +use std::time::Duration; + +fn main() { + let handle = thread::spawn(|| { + for i in 1..10 { + println!("hi number {i} from the spawned thread!"); + thread::sleep(Duration::from_millis(1)); + } + }); + + for i in 1..5 { + println!("hi number {i} from the main thread!"); + thread::sleep(Duration::from_millis(1)); + } + + // Waiting for all threads to finish using join handles + // Without the join below, threads stop prematurely + // because there is no guarantee on the order in which + // threads run, also can't guarantee spawned thread + // will get to run at all. + + // calling `join` on a handle blocks the thread currently + // running until the thread represented by `handle` + // terminates. + handle.join().unwrap(); + + // Using move closures with threads + // + // Often use move keyword with closures because + // closure takes ownership fo values it uses from environment. + + let v = vec![1, 2, 3]; + let handle = thread::spawn(move || { + println!("Here's a vector: {v:?}"); + }); + handle.join().unwrap(); +}