I try to use the indicatif crate to show several progress bars of sub-tasks together with one progress bar counting all finished tasks. Here is my code:
# Cargo.toml
[dependencies]
indicatif = "0.15.0"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
//! main.rs
use std::time::Duration;
use futures::{StreamExt, stream::futures_unordered::FuturesUnordered};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let m = MultiProgress::new();
let sty = ProgressStyle::default_bar()
.template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}")
.progress_chars("##-");
let total_pb = m.add(ProgressBar::new(3));
total_pb.set_style(sty.clone());
let mut futs = FuturesUnordered::new();
let durations = [15u64, 8, 3];
let mut pb_cnt = 0usize;
for &duration in durations.iter() {
let pb = m.insert(pb_cnt, ProgressBar::new(256));
pb_cnt += 1;
pb.set_style(sty.clone());
let total_pb = total_pb.clone();
let task = tokio::spawn(async move {
for i in 0i32..256 {
pb.set_message(&format!("item #{}", i + 1));
pb.inc(1);
tokio::time::sleep(Duration::from_millis(duration)).await;
}
pb.finish_with_message("done");
total_pb.inc(1);
});
futs.push(task);
}
while let Some(result) = futs.next().await {
result?;
}
total_pb.finish_with_message("done");
m.join()?;
// tokio::task::spawn_blocking(move || m.join().unwrap() ).await.unwrap();
Ok(())
}
total_pb
is the bar counting all finished tasks.
The problem is that no progress bars appear on the console until all work has finished already, only showing me their final states. I tried to follow suggestions in this issue and this issue, but there are a couple problems:
let m = tokio::task::spawn_blocking(move || m.join().unwrap());
as in the code above but it doesn't work.total_pb
won't finish until all tasks finish, which means m.join()
cannot be called right after each spawned task as an synchronization as suggested in the first issue.There are different solutions, you need to call m.join()
on an active blocking thread. Either you do it in your main thread, and so you need to move your:
while let Some(result) = futs.next().await {
result?;
}
somewhere else, like spawn a task.
Or you spawn a blocking thread and just await it at the end (probably the best). So just do:
let handle_m = tokio::task::spawn_blocking(move || m.join().unwrap()); // add this line
while let Some(result) = futs.next().await {
result?;
}
total_pb.finish_with_message("done");
handle_m.await?; // don't forget to await your handle
Note that contrary to your try #1, you must spawn the blocking thread BEFORE await your result...
There was 2 examples that could help you in the future tokio.rs multi.rs.