Skip to content

Latest commit

 

History

History
111 lines (78 loc) · 3.55 KB

File metadata and controls

111 lines (78 loc) · 3.55 KB

Concurrency

concurrency is the ability for different parts of a program to execute independently, parallel means execution at the same time and not the same with concurrency.

in concurrency, it splits multiple execution steps into different threads. example:

use std::thread;

let handle = thread::spawn(move || {
    println!("Hello from thread!");
});

threaded execution steps are not guaranteed, there is no way to tell exactly the thread that will finish first. however you can be able to block the main thread to wait for the threads to finish. example:

let v = vec![1, 2, 3];

let handle2 = thread::spawn(move || {
    println!("{:?}", v);
});

// block main thread and wait for execution
handle2.join().unwrap();

Channels

channels enables threads to facilitate message passing between each other. The channel has two ends transmitter and the receiver example:

use std::sync::mpsc;

let (transmitter, receiver) = mpsc::channel();
thread::spawn(move || transmitter.send(String::from("thread 1")).unwrap());
let tx1 = transmitter.clone();
thread::spawn(move || tx1.send(String::from("thread 2")).unwrap());
let tx2 = transmitter.clone();
thread::spawn(move || tx2.send(String::from("thread 3")).unwrap());
let tx3 = transmitter.clone();
thread::spawn(move || tx3.send(String::from("thread n")).unwrap());
let tx4 = transmitter.clone();
thread::spawn(move || tx4.send(String::from("thread n + 1")).unwrap());

for rec in receiver {
    println!("{}", rec);
}

however as many for optimality, there may be cases where the receiver may not be able to catch up with requests from the transmitters making the queue to be over-populated hence rust provides the sync_channel which allows you to specify the number of values a queue can hold. once it reaches it's limit, it can block requests until the receiver can receive more requests. example

let (transmitter, receiver) = mpsc::sync_channel(1000);

Send and Sync Traits

  • types that implement send are safe to pass by value to another thread and can be moved across threads
  • types that implement sync are safe to pass by non-mutable reference to another thread, and can be shared across threads.

example:

use std::sync::Arc;
use std::rc::Rc;

/// unsafe
let rc = Rc::new(String::from("Test"));
// safe
let rc1 = Arc::new(String::from("Test"));

let rc2 = rc1.clone();
thread::spawn(move || drop(rc2));

Shared State (Mutex)

message passing is one way to communicate between threads, another way is via mutual exclusion or mutex for short. mutex's allows only one thread to access data at a time using the lock the lock monitors which thread has access to data at a particular time. mutex's ensures 2 rules:

  • you must acquire a lock before using the data.
  • you must give back the lock after using the data.

a mutex is handy for mutating data that is shared between threads.

let counter = Arc::new(Mutex::new(0));
let mut handlex = vec![];

for _ in 0..8 {
    let counter = Arc::clone(&counter);
    let handl = thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1;
    });
    handlex.push(handl);
}

Mutex Poisoning

what will happen if the thread panicked while holding the lock?

when this happens, all other threads are not able to access the data since its already tainted.

Rayon

rayon is a lightweight data parallelize'r crate that helps us split our programs into parallel computations. By default the rayon uses the same number of threads as the logical cpu available otherwise it will jus use the number of physical CPUs