如何使用异步 indicatif 显示总计数器栏?

Ste*_*e Z 5 rust async-await rust-tokio

我尝试使用指示板条箱来显示子任务的多个进度条以及一个计算所有已完成任务的进度条。这是我的代码:

# Cargo.toml
[dependencies]
indicatif = "0.15.0"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
Run Code Online (Sandbox Code Playgroud)
//! 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(())
}
Run Code Online (Sandbox Code Playgroud)

total_pb是计算所有已完成任务的栏。

问题是,在所有工作完成之前,控制台上不会出现进度条,只向我显示它们的最终状态。我尝试遵循本期本期中的建议,但存在一些问题:

  1. 我尝试let m = tokio::task::spawn_blocking(move || m.join().unwrap());按照上面的代码插入,但它不起作用。
  2. total_pb在所有任务完成之前不会完成,这意味着m.join()不能在每个生成的任务之后立即调用作为第一期中建议的同步。

Sta*_*eur 3

有不同的解决方案,您需要调用m.join()活动的阻塞线程。要么在主线程中执行此操作,所以您需要移动:

while let Some(result) = futs.next().await {
    result?;
}
Run Code Online (Sandbox Code Playgroud)

其他地方,比如生成任务。

或者您生成一个阻塞线程并在最后等待它(可能是最好的)。所以就这样做:

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
Run Code Online (Sandbox Code Playgroud)

请注意,与您的尝试#1相反,您必须在等待结果之前生成阻塞线程...

有 2 个示例可以在将来为您提供帮助tokio.rs multi.rs