// ix Developer 2019 - Moderne Softwareentwicklung // Jens Breitbart, Stefan Lankes, Rust als sichere Programmiersprache fuer // systemnahe und parallele Software, S. 20-24. // https://tha.de/homes/hhoegl/home/sysprog/ix-developer/ix-developer-19-20.pdf // hhoegl, 2021-09-28 use std::sync::Arc; use std::sync::Mutex; use std::thread; const NUM_STEPS: usize = 1000; fn variante1() -> f64 { let step = 1.0 / NUM_STEPS as f64; let nthreads = 4; let sum = Arc::new(Mutex::new(0.0 as f64)); let result: f64; let handles: Vec<_> = (0..nthreads) .map(|tid| { let sum = sum.clone(); thread::spawn(move || { let start = (NUM_STEPS / nthreads) * tid; let end = (NUM_STEPS / nthreads) * (tid + 1); let mut v: f64; for i in start..end { let x = (i as f64 + 0.5) * step; v = 4.0 / (1.0 + x * x); *sum.lock().unwrap() += v; } //println!(" thread {} => {}", tid, *sum.lock().unwrap()); }) }) .collect(); for h in handles { h.join().unwrap(); } //println!("{:?}", handles); // <-- Fehler: value borrowed here after move //println!(" sum after join: {:?}", *sum.lock().unwrap()); result = *sum.lock().unwrap(); result } // Wie kann man partial_sum aus den Threads zurueckgeben und aufsummieren ueber // alle Threads? fn variante2() -> f64 { let step = 1.0 / NUM_STEPS as f64; let nthreads = 4; let mut sum = 0.0 as f64; let handles: Vec<_> = (0..nthreads) .map(|tid| { thread::spawn(move || { let start = (NUM_STEPS / nthreads) * tid; let end = (NUM_STEPS / nthreads) * (tid + 1); let mut partial_sum = 0 as f64; for i in start..end { let x = (i as f64 + 0.5) * step; partial_sum += 4.0 / (1.0 + x * x); } partial_sum }) }) .collect(); // So geht es NICHT // println!("{:?}", handles[0].join().unwrap()); // move occurs because value has type `JoinHandle`, which does not // implement the `Copy` trait // So einfach ist die Loesung! // siehe https://users.rust-lang.org/t/how-to-join-handles-of-threads/52494 let mut s; // besser als: let mut s = 0.0 as f64; for h in handles { // Der Typ von h ist 'struct JoinHandle'. Schleife ueber owned // JoinHandle. // // Wenn man mit handles.iter() iteriert, bekommt man fuer h den // Typ &JoinHandle. Damit bekommt man wieder die "move" // Fehlermeldung. s = h.join().unwrap(); // println!("Variante 2: {:?}", s); sum += s; } sum } // Rayon use rayon::prelude::*; fn variante3() -> f64 { let step = 1.0 / NUM_STEPS as f64; let sum: f64 = (0..NUM_STEPS) .into_par_iter() .map(|i| { let x = (i as f64 + 0.5) * step; 4.0 / (1.0 + x * x) }) .sum(); sum } // Channels use std::sync::mpsc; fn variante4() -> f64 { let step = 1.0 / NUM_STEPS as f64; let nthreads = 4; let (tx, rx) = mpsc::channel::(); let mut sum = 0.0; for id in 0..nthreads { let thread_tx = tx.clone(); let start = (NUM_STEPS / nthreads) * id; let end = (NUM_STEPS / nthreads) * (id + 1); thread::spawn(move || { let mut partial_sum: f64 = 0.0; for i in start..end { let x = (i as f64 + 0.5) * step; partial_sum += 4.0 / (1.0 + x * x); } thread_tx.send(partial_sum).unwrap(); }); } for _ in 0..nthreads { sum = sum + rx.recv().unwrap(); } sum } fn main() { let mut sum; sum = variante1(); println!("Variante 1 = {}", sum); sum = variante2(); println!("Variante 2 = {}", sum); sum = variante3(); println!("Variante 3 = {}", sum); sum = variante4(); println!("Variante 4 = {}", sum); }