我的目标是同时运行N个函数,但在它们全部完成之前不希望产生更多函数。这是what I have so far:
extern crate tokio;
extern crate futures;
use futures::future::lazy;
use std::{thread, time};
use tokio::prelude::*;
use tokio::timer::Interval;
fn main() {
let task = Interval::new(time::Instant::now(), time::Duration::new(1, 0))
.for_each(|interval| {
println!("Interval: {:?}", interval);
for i in 0..5 {
tokio::spawn(lazy(move || {
println!("Hello from task {}", i);
// mock delay (something blocking)
// thread::sleep(time::Duration::from_secs(3));
Command::new("sleep").arg("3").output().expect("failed to execute process");
Ok(())
}));
}
Ok(())
})
.map_err(|e| panic!("interval errored; err={:?}", e));
tokio::run(task);
}
我每秒都会生成5个函数,但是现在我想等到所有功能都完成后再生成更多函数。
根据我的理解(可能是我弄错了主意),我正在另一个未来中返回Future
task (Interval ----------------------+ (outer future)
for i in 0..5 { |
tokio::spawn( ----+ |
// my function | (inner) |
Ok(()) | |
) ----+ |
} |
Ok(()) --------------------------+
我被困在等待内心的未来完成。
答案 0 :(得分:2)
您可以通过加入您的工人期货来实现这一点,使它们全部并行运行,但必须全部完成。然后,出于相同的理由,您可以延迟1秒加入。将其包装成一个循环以永久运行(对于该演示,则为5次迭代)。
use futures::future::{self, Loop}; // 0.1.26
use std::time::{Duration, Instant};
use tokio::{prelude::*, timer::Delay}; // 0.1.18
fn main() {
let repeat_count = Some(5);
let forever = future::loop_fn(repeat_count, |repeat_count| {
eprintln!("Loop starting at {:?}", Instant::now());
// Resolves when all pages are done
let batch_of_pages = future::join_all(all_pages());
// Resolves when both all pages and a delay of 1 second is done
let wait = Future::join(batch_of_pages, ez_delay_ms(1000));
// Run all this again
wait.map(move |_| {
if let Some(0) = repeat_count {
Loop::Break(())
} else {
Loop::Continue(repeat_count.map(|c| c - 1))
}
})
});
tokio::run(forever.map_err(drop));
}
fn all_pages() -> Vec<Box<dyn Future<Item = (), Error = ()> + Send + 'static>> {
vec![Box::new(page("a", 100)), Box::new(page("b", 200))]
}
fn page(name: &'static str, time_ms: u64) -> impl Future<Item = (), Error = ()> + Send + 'static {
future::ok(())
.inspect(move |_| eprintln!("page {} starting", name))
.and_then(move |_| ez_delay_ms(time_ms))
.inspect(move |_| eprintln!("page {} done", name))
}
fn ez_delay_ms(ms: u64) -> impl Future<Item = (), Error = ()> + Send + 'static {
Delay::new(Instant::now() + Duration::from_millis(ms)).map_err(drop)
}
Loop starting at Instant { tv_sec: 4031391, tv_nsec: 806352322 }
page a starting
page b starting
page a done
page b done
Loop starting at Instant { tv_sec: 4031392, tv_nsec: 807792559 }
page a starting
page b starting
page a done
page b done
Loop starting at Instant { tv_sec: 4031393, tv_nsec: 809117958 }
page a starting
page b starting
page a done
page b done
Loop starting at Instant { tv_sec: 4031394, tv_nsec: 813142458 }
page a starting
page b starting
page a done
page b done
Loop starting at Instant { tv_sec: 4031395, tv_nsec: 814407116 }
page a starting
page b starting
page a done
page b done
Loop starting at Instant { tv_sec: 4031396, tv_nsec: 815342642 }
page a starting
page b starting
page a done
page b done
另请参阅:
答案 1 :(得分:1)
根据我的理解(我可能会把想法弄错了),我是 在另一个未来返回
Future
您没看错,但是在您提供的代码中,唯一返回的未来是实现Ok(())
的{{1}}。 IntoFuture
只是将新任务生成到Tokio的tokio::spawn
中。
如果我从您的问题中了解到,您想在上一个完成后生成下一个批次,但是如果前一个在1秒之前完成在产生下一批之前完成那一秒。
实现自己的未来并自己进行民意测验将是一个更好的解决方案,但这可以粗略地做到:
DefaultExecutor
来收集批处理任务。这是一个新的期货,等待收集的期货完成。以下是代码(Playground):
join_all
输出:
extern crate futures;
extern crate tokio;
use futures::future::lazy;
use std::time::{self, Duration, Instant};
use tokio::prelude::*;
use tokio::timer::{Delay, Interval};
use futures::future::join_all;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
fn main() {
let locker = Arc::new(AtomicBool::new(false));
let task = Interval::new(time::Instant::now(), time::Duration::new(1, 0))
.map_err(|e| panic!("interval errored; err={:?}", e))
.for_each(move |interval| {
let is_locked = locker.load(Ordering::SeqCst);
println!("Interval: {:?} --- {:?}", interval, is_locked);
if !is_locked {
locker.store(true, Ordering::SeqCst);
println!("locked");
let futures: Vec<_> = (0..5)
.map(|i| {
lazy(move || {
println!("Running Task-{}", i);
// mock delay
Delay::new(Instant::now() + Duration::from_millis(100 - i))
.then(|_| Ok(()))
})
.and_then(move |_| {
println!("Task-{} is done", i);
Ok(())
})
})
.collect();
let unlocker = locker.clone();
tokio::spawn(join_all(futures).and_then(move |_| {
unlocker.store(false, Ordering::SeqCst);
println!("unlocked");
Ok(())
}));
}
Ok(())
});
tokio::run(task.then(|_| Ok(())));
}
警告! :,请检查Shepmaster's comment
即使是示范,you should not use thread:sleep在未来。 有更好的选择