使用期货和Tokio进行Rust并发执行

时间:2018-11-18 05:47:18

标签: rust future rust-tokio

我有一些目前看起来像这样的Rust代码

fn read_stdin(mut tx: mpsc::Sender<String>) {
    loop {
        // read from stdin and send value over tx.
    }
}

fn sleep_for(n: u64) -> impl Future<Item = (), Error = ()> {
    thread::sleep(time::Duration::from_millis(n));
    println!("[{}] slept for {} ms", Local::now().format("%T%.3f"), n);
    future::ok(())
}

fn main() {
    let (stdin_tx, stdin_rx) = mpsc::channel(0);
    thread::spawn(move || read_stdin(stdin_tx));

    let server = stdin_rx
        .map(|data| data.trim().parse::<u64>().unwrap_or(0))
        .for_each(|n| tokio::spawn(sleep_for(n * 100)));
    tokio::run(server);
}

它使用tokio和Futures,目的是运行一些“ cpu繁重”的工作(由sleep_for函数模拟),然后将一些东西输出到stdout

当我运行它时,看起来一切正常,我得到了这个输出

2
[00:00:00.800] slept for 200 ms
10
1
[00:00:01.800] slept for 1000 ms
[00:00:01.900] slept for 100 ms

第一个值为2的输出与预期完全一样,我看到200毫秒后打印的时间戳。但是对于接下来的输入,很明显sleep_for函数是顺序执行的,而不是并发执行的。

我想看的输出是

2
[00:00:00.800] slept for 200 ms
10
1
[00:00:00.900] slept for 100 ms
[00:00:01.900] slept for 1000 ms

似乎要获得我想要的输出,我想同时执行sleep_for(10)sleep_for(1)。我将如何使用Rust和Tokio在Rust中进行此操作?

(注意:时间戳的实际值并不重要,我会更多地使用它们来显示程序中执行的顺序)

1 个答案:

答案 0 :(得分:0)

找到了使用futures-timer条板箱的解决方案。

use chrono::Local;
use futures::{future, sync::mpsc, Future, Sink, Stream};
use futures_timer::Delay;
use std::{io::stdin, thread, time::Duration};

fn read_stdin(mut tx: mpsc::Sender<String>) {
    let stdin = stdin();
    loop {
        let mut buf = String::new();
        stdin.read_line(&mut buf).unwrap();
        tx = tx.send(buf).wait().unwrap()
    }
}

fn main() {
    let (stdin_tx, stdin_rx) = mpsc::channel(0);
    thread::spawn(move || read_stdin(stdin_tx));

    let server = stdin_rx
        .map(|data| data.trim().parse::<u64>().unwrap_or(0) * 100)
        .for_each(|delay| {
            println!("[{}] {} ms -> start", Local::now().format("%T%.3f"), delay);
            tokio::spawn({
                Delay::new(Duration::from_millis(delay))
                    .and_then(move |_| {
                        println!("[{}] {} ms -> done", Local::now().format("%T%.3f"), delay);
                        future::ok(())
                    })
                    .map_err(|e| panic!(e))
            })
        });

    tokio::run(server);
}

问题在于,宁可让未来停滞不前然后通知当前任务,问题中显示的代码只是使线程处于休眠状态,因此无法取得任何进展。

更新:现在我遇到了tokio-timer,这似乎是实现此目的的标准方法。