Write threaded async code with least number of crates

I am learning to write async programs. Need guidance from where to learn.

Example use case:

  1. Counter increasing 1 every second in the background within (0-9)
  2. Another counter picking random values between 4 and 9 at an interval of 1.5 seconds
  3. Spawn 4 independent processes (preferably low cost), take the counter values as 1st and 2nd digits and output the ASCII characters sequentially. 1st thread should retain and update values every 1 second, 2nd thread should retain and update values every 2 seconds, and so on.

e.g.: If the 1st counter value is 5 and the 2nd counter value is 6, then the output should be A. Similarly, there should be other characters and shown as output like A4Ob

Is this possible? If yes, require pseudocode for the same. One without ordering the processes, and another with ordering (i.e. 1st process output should be shown 1st)


Also how to sync multiple async functions? i.e. can one async function use another a normal sync function? If yes, how to convert the output of the normal sync function to a future?


I am willing to learn and invest time into this. Constructive ideas will suffice

You can technically do all of this with just std if you’re willing to write the unsafe code necessary to write your own executor. Otherwise, you’ll want to use something like tokio to actually manage all of the tasks. The tokio::time module has utilities for waiting in async functions.

Alternately, you could do this with ordinary threads pretty easily, without the async machinery. You can use an MPSC channel to let the various producer threads send their results to a single thread responsible for printing results.

Thanks for your input. I feel MPSC would be the way to go for my use case, tokio would be overkill. Is there any way to improve the performance by preventing re-allocation of threads of a specific function continuously if I know that they will run 90% of the time the app is running? In other words, keeping a channel open for that specific function on standby all the time.

I am not comfortable with unstable rust yet, so that's a nogo.

Also, where do I learn to write futures which can be called from async functions?

I don't think you'd need a channel here at all, just a few atomics for each counter. Something like:

struct State {
    counter_1: AtomicU8,
    counter_2: AtomicU8,
}

let state = Arc::new(State {
    counter_1: AtomicU8::new(0),
    counter_2: AtomicU8::new(gen_counter_2()),
});

thread::spawn({
    let state = Arc::clone(&state);
    let mut counter = 0;
    move || loop {
        thread::sleep(Duration::from_secs(1));
        counter = (counter + 1) % 10;
        state.counter_1.store(counter, atomic::Ordering::Relaxed);
    }
});

You can write similar code for the other five threads.

Note that unless you use Tokio you won't need to do this - but if you mark any function async fn it will return a future when called, which can be .awaited to actually run the function body.

3 Likes