By deterministic, I am referring to their scheduling. Here's an example in Rust:
use std::thread;
use std::sync::mpsc::channel;
fn main() {
let thread_count = 4;
let (tx, rx) = channel();
for x in 0..thread_count {
let tx = tx.clone();
thread::spawn(move || {
println!("t{} tx", x);
tx.send(x).unwrap();
});
}
for _ in 0..thread_count {
println!("rx from t{}", rx.recv().unwrap());
}
}
What I'm doing there is create 4 child threads, from which I send messages that are to be captured by the main thread.
I built and ran it with:
rustc --opt-level 0 main.rs && ./main
Following is output from one sample run:
t1 tx
t0 tx
t2 tx
t3 tx
rx from t1
rx from t0
rx from t3
rx from t2
Following is what I get with another run:
t0 tx
t1 tx
rx from t1
t2 tx
rx from t2
t3 tx
rx from t3
rx from t0
If it was all deterministic (i.e. predictable), t0 tx
would always
come before t1 tx
, and rx from t0
before rx from t1
, and so on.
I think this is done for performance reasons, where the kernel just looks for an available slot, running each thread on a best-effort basis. My guess is that a more real-time kernel would be more predictable.