Hello everyone, new Rust lang series episode is here. Topic of this episode is working with additional threads.
As even mid-range mobile phones CPU today contain multiple cores and each of them is individual processing units it's good to know how to utilize them.
So far all our code was running on single thread and everything was executed in main thread. If we want to utilize more of multi-core CPU potential, we have to think about concurrency and splitting processing between additional threads.
Its worth to say that most Rust types are not thread-safe by default. That means they cannot assure reliable behavior in multi-thread environment. There are special traits in Rust which provides key guarantees.
We will create three additional threads which will print greeting.
use std::thread;
use std::time::Duration;
fn main() {
for i in 1..4 {
thread::spawn(move || {
println!("Hello Steemit from thread {}", i);
});
}
thread::sleep(Duration::from_millis(100));
println!("All work done!")
}
Hello Steemit from thread 1
Hello Steemit from thread 2
Hello Steemit from thread 3
All work done!
std::thread provides API for creating additional threads
spawn() is method for creating thread with given closure. Thread is creating and running method returns thread JoinHandle.
sleep() thread method puts current thread to sleep for Duration time. In this case it makes main thread sleeping to give some time to other threads to finish.
This is more complicated because we need to assure atomicity, that means that each data update by each thread is done correctly and there is no possibility to get invalid data or out-of-sync writing.
In example below we will take vector which will be correctly updated (vector data will be incremented) by five individual threads.
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
let data_arc = Arc::new(Mutex::new(vec![0,0,0]));
for i in 0..5 {
let data_arc_for_thread = data_arc.clone();
thread::spawn(move || {
let mut data_guard = data_arc_for_thread.lock().unwrap();
for j in 0..3 {
data_guard[j] += i;
}
println!("thread {} work done", i);
});
}
thread::sleep(Duration::from_millis(100));
println!("Final result from parallel computation: {:?}", data_arc);
}
Output can be something like this (processing order can differ):
thread 1 work done
thread 3 work done
thread 0 work done
thread 4 work done
thread 2 work done
Final result from parallel computation: Mutex { data: [10, 10, 10] }
Arc is atomic-Rc, marked as Send.
Provides mutual-exclusion via RAII guards (object-like guards)
lock() method acquires a mutex guard, blocking the current thread until it's able to get it. Mutex is unlocked as guard goes out of scope. Returns LockResult
type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
That's all for now, thank you for your appreciations, feel free to comment and point out possible mistakes (first 24 hours works the best but any time is fine). May Jesus bless your programming skills, use them wisely and see you next time.
Meanwhile you can also check the official documentation for additional related information: