Going through rustlings for the first time

ex(20): threads

authored by chay.dev and committed by

Chay Choong 84051300 0b9f897e

+151 -13
+5 -2
.rustlings-state.txt
··· 1 1 DON'T EDIT THIS FILE! 2 2 3 - threads1 3 + macros1 4 4 5 5 intro1 6 6 intro2 ··· 80 80 box1 81 81 rc1 82 82 arc1 83 - cow1 83 + cow1 84 + threads1 85 + threads2 86 + threads3
+1
exercises/20_threads/threads1.rs
··· 24 24 for handle in handles { 25 25 // TODO: Collect the results of all threads into the `results` vector. 26 26 // Use the `JoinHandle` struct which is returned by `thread::spawn`. 27 + results.push(handle.join().unwrap()); 27 28 } 28 29 29 30 if results.len() != 10 {
+8 -4
exercises/20_threads/threads2.rs
··· 2 2 // work. But this time, the spawned threads need to be in charge of updating a 3 3 // shared value: `JobStatus.jobs_done` 4 4 5 - use std::{sync::Arc, thread, time::Duration}; 5 + use std::{ 6 + sync::{Arc, Mutex}, 7 + thread, 8 + time::Duration, 9 + }; 6 10 7 11 struct JobStatus { 8 12 jobs_done: u32, ··· 10 14 11 15 fn main() { 12 16 // TODO: `Arc` isn't enough if you want a **mutable** shared state. 13 - let status = Arc::new(JobStatus { jobs_done: 0 }); 17 + let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 })); 14 18 15 19 let mut handles = Vec::new(); 16 20 for _ in 0..10 { ··· 19 23 thread::sleep(Duration::from_millis(250)); 20 24 21 25 // TODO: You must take an action before you update a shared value. 22 - status_shared.jobs_done += 1; 26 + status_shared.lock().unwrap().jobs_done += 1; 23 27 }); 24 28 handles.push(handle); 25 29 } ··· 30 34 } 31 35 32 36 // TODO: Print the value of `JobStatus.jobs_done`. 33 - println!("Jobs done: {}", todo!()); 37 + println!("Jobs done: {}", status.lock().unwrap().jobs_done); 34 38 }
+3 -1
exercises/20_threads/threads3.rs
··· 17 17 fn send_tx(q: Queue, tx: mpsc::Sender<u32>) { 18 18 // TODO: We want to send `tx` to both threads. But currently, it is moved 19 19 // into the first thread. How could you solve this problem? 20 + let tx2 = tx.clone(); 21 + 20 22 thread::spawn(move || { 21 23 for val in q.first_half { 22 24 println!("Sending {val:?}"); ··· 28 30 thread::spawn(move || { 29 31 for val in q.second_half { 30 32 println!("Sending {val:?}"); 31 - tx.send(val).unwrap(); 33 + tx2.send(val).unwrap(); 32 34 thread::sleep(Duration::from_millis(250)); 33 35 } 34 36 });
+35 -2
solutions/20_threads/threads1.rs
··· 1 + // This program spawns multiple threads that each runs for at least 250ms, and 2 + // each thread returns how much time it took to complete. The program should 3 + // wait until all the spawned threads have finished and should collect their 4 + // return values into a vector. 5 + 6 + use std::{ 7 + thread, 8 + time::{Duration, Instant}, 9 + }; 10 + 1 11 fn main() { 2 - // DON'T EDIT THIS SOLUTION FILE! 3 - // It will be automatically filled after you finish the exercise. 12 + let mut handles = Vec::new(); 13 + for i in 0..10 { 14 + let handle = thread::spawn(move || { 15 + let start = Instant::now(); 16 + thread::sleep(Duration::from_millis(250)); 17 + println!("Thread {i} done"); 18 + start.elapsed().as_millis() 19 + }); 20 + handles.push(handle); 21 + } 22 + 23 + let mut results = Vec::new(); 24 + for handle in handles { 25 + // Collect the results of all threads into the `results` vector. 26 + results.push(handle.join().unwrap()); 27 + } 28 + 29 + if results.len() != 10 { 30 + panic!("Oh no! Some thread isn't done yet!"); 31 + } 32 + 33 + println!(); 34 + for (i, result) in results.into_iter().enumerate() { 35 + println!("Thread {i} took {result}ms"); 36 + } 4 37 }
+39 -2
solutions/20_threads/threads2.rs
··· 1 + // Building on the last exercise, we want all of the threads to complete their 2 + // work. But this time, the spawned threads need to be in charge of updating a 3 + // shared value: `JobStatus.jobs_done` 4 + 5 + use std::{ 6 + sync::{Arc, Mutex}, 7 + thread, 8 + time::Duration, 9 + }; 10 + 11 + struct JobStatus { 12 + jobs_done: u32, 13 + } 14 + 1 15 fn main() { 2 - // DON'T EDIT THIS SOLUTION FILE! 3 - // It will be automatically filled after you finish the exercise. 16 + // `Arc` isn't enough if you want a **mutable** shared state. 17 + // We need to wrap the value with a `Mutex`. 18 + let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 })); 19 + // ^^^^^^^^^^^ ^ 20 + 21 + let mut handles = Vec::new(); 22 + for _ in 0..10 { 23 + let status_shared = Arc::clone(&status); 24 + let handle = thread::spawn(move || { 25 + thread::sleep(Duration::from_millis(250)); 26 + 27 + // Lock before you update a shared value. 28 + status_shared.lock().unwrap().jobs_done += 1; 29 + // ^^^^^^^^^^^^^^^^ 30 + }); 31 + handles.push(handle); 32 + } 33 + 34 + // Waiting for all jobs to complete. 35 + for handle in handles { 36 + handle.join().unwrap(); 37 + } 38 + 39 + println!("Jobs done: {}", status.lock().unwrap().jobs_done); 40 + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 4 41 }
+60 -2
solutions/20_threads/threads3.rs
··· 1 + use std::{sync::mpsc, thread, time::Duration}; 2 + 3 + struct Queue { 4 + first_half: Vec<u32>, 5 + second_half: Vec<u32>, 6 + } 7 + 8 + impl Queue { 9 + fn new() -> Self { 10 + Self { 11 + first_half: vec![1, 2, 3, 4, 5], 12 + second_half: vec![6, 7, 8, 9, 10], 13 + } 14 + } 15 + } 16 + 17 + fn send_tx(q: Queue, tx: mpsc::Sender<u32>) { 18 + // Clone the sender `tx` first. 19 + let tx_clone = tx.clone(); 20 + thread::spawn(move || { 21 + for val in q.first_half { 22 + println!("Sending {val:?}"); 23 + // Then use the clone in the first thread. This means that 24 + // `tx_clone` is moved to the first thread and `tx` to the second. 25 + tx_clone.send(val).unwrap(); 26 + thread::sleep(Duration::from_millis(250)); 27 + } 28 + }); 29 + 30 + thread::spawn(move || { 31 + for val in q.second_half { 32 + println!("Sending {val:?}"); 33 + tx.send(val).unwrap(); 34 + thread::sleep(Duration::from_millis(250)); 35 + } 36 + }); 37 + } 38 + 1 39 fn main() { 2 - // DON'T EDIT THIS SOLUTION FILE! 3 - // It will be automatically filled after you finish the exercise. 40 + // You can optionally experiment here. 41 + } 42 + 43 + #[cfg(test)] 44 + mod tests { 45 + use super::*; 46 + 47 + #[test] 48 + fn threads3() { 49 + let (tx, rx) = mpsc::channel(); 50 + let queue = Queue::new(); 51 + 52 + send_tx(queue, tx); 53 + 54 + let mut received = Vec::with_capacity(10); 55 + for value in rx { 56 + received.push(value); 57 + } 58 + 59 + received.sort(); 60 + assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 61 + } 4 62 }