0

I would like to run an arbitrary number of futures concurrently in Rust. For this, I created a simple program that simulates real work by calling thread::sleep.

The calc() function below pretends to do work by sleeping for 2 seconds, then the batch() function calls it 3 times, I'm assuming, asynchronously via join_all.

The problem is that this program executes for 6 seconds, not for 2 as I would expect.

What is the trick to make the futures in the vec! run asynchronously?

use futures::future::join_all;
use futures::executor::block_on;
use std::thread;
use std::time::{Duration, SystemTime};

async fn calc(i: u32) -> u32 { 
    let seconds = Duration::from_secs(2);
    thread::sleep(seconds);
    i 
}

async fn batch() -> Vec<u32> {
    let futures = vec![calc(1), calc(2), calc(3)];
    join_all(futures).await
}

fn main() {
    let now = SystemTime::now();
    let result = block_on(batch());
    println!("Result: {:?} took {:?} seconds", result, now.elapsed().unwrap());
}
sge
  • 170
  • 7
  • 3
    `thread::sleep` is a synchronous sleep, not an asynchronous one, so they run sequentially. `async` does not magically make your program concurrent. – Andrew Sun Dec 19 '19 at 04:59
  • @AndrewSun: Then what's the point of having async functions at all if everything inside them must be asynchronous? What if the `thread::sleep` was a long SQL operation? – sge Dec 19 '19 at 05:44
  • @sge see the doc "An implementation of poll should strive to return quickly, and should not block. Returning quickly prevents unnecessarily clogging up threads or event loops. If it is known ahead of time that a call to poll may end up taking awhile, the work should be offloaded to a thread pool (or something similar) to ensure that poll can return quickly." https://doc.rust-lang.org/std/future/trait.Future.html. however a SQL operation will need to wait for socket resource so you will not need any thread just wait that the SQL server response and that what await do. – Stargateur Dec 19 '19 at 06:14
  • Executor needs to act differently if one of the task is blocking, tokio executor has a solution: annotating the task as [blocking](https://docs.rs/tokio-threadpool/0.1.17/tokio_threadpool/fn.blocking.html). But it still has some restrictions, like if you use it with a combinator it blocks other tasks' execution in the combinator or if you use it in single threaded executor it would block the entire executor, please see `tokio::io::stdin` case which is annotated as blocking : https://stackoverflow.com/a/57591250 – Ömer Erden Dec 19 '19 at 06:27
  • @Stargateur: I tried the same code with an SQL query of `SELECT SLEEP(2)` instead of `thread::sleep(2)`. I still get the same result -- the program runs in 6 seconds and each element in the `futures` Vec is run sequentially, instead of concurrently. I'm not quite sure I understand how Rust's concurrency works. Any help would be greatly appreciated. – sge Dec 19 '19 at 19:18

0 Answers0