2

下面的程序应该从多个线程定期打印,但tokio::time::sleep没有按我预期的那样工作:

use tokio::prelude::*; //0.3.4
use tokio::runtime::Builder;
use tokio::time::Duration;

fn main() {
    let rt = Builder::new_multi_thread()
        .enable_all()
        .thread_stack_size(3 * 1024 * 1024)
        .build()
        .unwrap();

    rt.block_on(async {
        tokio::spawn(print_thread(1));
        tokio::spawn(print_thread(2));
        tokio::spawn(print_thread(3));
        tokio::spawn(print_thread(4));
    });
}

async fn print_thread(thread_num: usize) {
    tokio::spawn(async move {
        println!("thread{}-start", thread_num);
        loop {
            tokio::time::sleep(Duration::from_millis(1000)).await;
            println!("thread{}-running", thread_num);
        }
        println!("thread{}-start", thread_num);
    });
}

它的输出是

$cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.64s
     Running `target/debug/time_test`
thread1-start
thread2-start
thread3-start
thread4-start
$

我希望看到消息threadN-running,但没有输出。我不知道为什么程序突然退出。有人可以告诉我原因吗?

我的Cargo.toml样子是这样的:

[package]
name = "time_test"
version = "0.1.0"
edition = "2018"

[dependencies]
tokio = { version = "0.3", features = ["full"] }

另外,我的货物版本是:

$ cargo --version
cargo 1.48.0 (65cbdd2dc 2020-10-14)
4

1 回答 1

2

async块:

rt.block_on(async { ... })

不加入生成的未来,所以它在生成后立即结束。在内部有时间和之前,这结束了block_on和。任务在完成执行之前被杀死。maintasksleepprintln!

您可以通过将 包裹block_on在一个永无止境的循环中来解决这个问题:

loop {
  rt.block_on(async {
    tokio::spawn(print_thread(1));
    tokio::spawn(print_thread(2));
    tokio::spawn(print_thread(3));
    tokio::spawn(print_thread(4));
  });
}

现在,tokio 执行器拥有生成 , 并将消息打印到控制台所需的所有tasks时间sleep。但是,这loop将是昂贵的 cpu 明智的。另一种选择是使用std::future::pending自 Rust 1.48 起稳定的。这将创建一个永远不会解决的未来,代表一个永远不会完成的计算:

rt.block_on(async {
 ...
});

std::future::pending().await;
unreachable!();

此外,正如@AlexLarionov 所提到的,您实际上是在产卵task两次:

// 1st task
tokio::spawn(print_thread(1))

// 2nd task
async fn print_thread(thread_num: usize) {
    tokio::spawn(async move { ... });
}

第二次生成是不必要的,您的print_thread功能可以简化为以下内容:

async fn print_thread(thread_num: usize) {
    println!("thread{}-start", thread_num);
    loop {
        tokio::time::sleep(Duration::from_millis(1000)).await;
        println!("thread{}-running", thread_num);
    }
    // Note that this is unreachable
    println!("thread{}-start", thread_num);
}

请注意,对于用例tokio::time::sleep可能不是最佳选择。要按计划定期运行某些内容,您可以使用tokoi::time::interval

async fn print_thread(thread_num: usize) {
    println!("thread{}-start", thread_num);
    let mut interval = tokio::time::interval(Duration::from_millis(100));
    loop {
      interval.tick().await;
      println!("thread{}-running", thread_num);
    }
}
于 2020-11-26T14:40:52.443 回答