task::spawn_blocking(async {
std::thread::sleep(Duration::from_secs(1000));
});
Blocking is an overloaded term
Blocking API: an API that might force pre-emption
Blocked Task: A task that runs for too long
Blocking APIs are generally faster
Determining if a task really blocks is hard
It’s hard to determine for a full program if all instances of a task are staying under a certain max execution time.
spawn_blocking
spawn_blocking
is usually the solution for dealing with slightly longer tasks
task::spawn_blocking(async {
std::thread::sleep(Duration::from_secs(1000));
});
Separation of async and sync parts for benchmarking
Runtime monitoring, mostly through tracing.
Channels allow communication between tasks
This allows weak binding between components
All channels work through Ownership
Threading can be a lot faster in high-throughput situations
Threading deschedules automatically if threads run out of their timeslice
Async makes it much cheaper to hold slow and sleepy connections
Async is very good in reactive models
Full async
async at the edge
Multiple reactors
let (s, r) = mpsc::channel(32);
assert_eq!(s.send("Hello").await, Ok(()));
assert_eq!(r.recv().await, Ok("Hello"));
Bounded
Unbounded
Single Producer, Single Consumer (SPSC)
Multiple Producers, Single Consumer (MPSC)
Multiple Producers, Multiple Consumers (MPMC)
One-Shot
Pick a default one, preferably MPMC. Be liberal in using others when needed.
Avoid std::sync types - they preempt
There’s and async_std::sync
module with API equivalents
Pick types based on your usage pattern
e.g. RWLocks if Writes are common and reads rare
Mutex for other situations
Fairness comes into play here
Channels act as a natural synchronisation method, as they are read from 1 by 1.
Fairness describes the property of a combinator to make sure that every side is equally served. If one is not, it may starve.