什么是解决未来需要多长时间的干净方法?
What's a clean way to get how long a future takes to resolve?
我正在和 Tokio 一起做一些 UDP 的事情。
我想记录我的 UDP 探测未来需要花费的时间来解决。我想出了以下函数 time_future()
来包装未来并给我结果和持续时间。这个函数看起来很幼稚,我认为 Rust 有能力更清晰地表达这个概念。
我的工作代码(Playground):
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.11
use std::time::{Duration, Instant};
use futures::future::{lazy, ok};
use futures::Future;
use tokio::runtime::current_thread::Runtime;
use tokio::timer::Delay;
struct TimedFutureResult<T, E> {
elapsed: Duration,
result: Result<T, E>,
}
impl<T, E> TimedFutureResult<T, E> {
pub fn elapsed_ms(&self) -> i64 {
return (self.elapsed.as_secs() * 1000 + (self.elapsed.subsec_nanos() / 1000000) as u64)
as i64;
}
}
fn time_future<F: Future>(f: F) -> impl Future<Item = TimedFutureResult<F::Item, F::Error>> {
lazy(|| {
let start = Instant::now();
f.then(move |result| {
ok::<TimedFutureResult<F::Item, F::Error>, ()>(TimedFutureResult {
elapsed: start.elapsed(),
result: result,
})
})
})
}
fn main() {
let when = Instant::now() + Duration::from_millis(100);
let f = time_future(Delay::new(when)).then(|r| match r {
Ok(r) => {
println!("resolved in {}ms", r.elapsed_ms());
r.result
}
_ => unreachable!(),
});
let mut runtime = Runtime::new().unwrap();
runtime.block_on(f).unwrap();
}
我怎样才能改进它并使其更加地道?我能否以某种方式让界面像 inspect()
或 then()
一样工作?
Delay::new(when)
.timed(|res, elapsed| println!("{}ms!", elapsed))
.and_then(...);
我尝试创建一个 Timed
特征并为 Future
实现它,但我对自己的工作方式一点信心都没有。这些类型真的让我大吃一惊。
我至少找对了树吗?
编写 future 的动作很简单,添加一个可链接的方法与 中所示的技术相同。
唯一真正棘手的方面是决定时间何时开始——是在创建未来还是在首次轮询时?
我选择在第一次轮询时使用,因为这似乎更有用:
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.11
use std::time::{Duration, Instant};
use futures::{try_ready, Async, Future, Poll};
use tokio::{runtime::current_thread::Runtime, timer::Delay};
struct Timed<Fut, F>
where
Fut: Future,
F: FnMut(&Fut::Item, Duration),
{
inner: Fut,
f: F,
start: Option<Instant>,
}
impl<Fut, F> Future for Timed<Fut, F>
where
Fut: Future,
F: FnMut(&Fut::Item, Duration),
{
type Item = Fut::Item;
type Error = Fut::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let start = self.start.get_or_insert_with(Instant::now);
let v = try_ready!(self.inner.poll());
let elapsed = start.elapsed();
(self.f)(&v, elapsed);
Ok(Async::Ready(v))
}
}
trait TimedExt: Sized + Future {
fn timed<F>(self, f: F) -> Timed<Self, F>
where
F: FnMut(&Self::Item, Duration),
{
Timed {
inner: self,
f,
start: None,
}
}
}
impl<F: Future> TimedExt for F {}
fn main() {
let when = Instant::now() + Duration::from_millis(100);
let f = Delay::new(when).timed(|res, elapsed| println!("{:?} elapsed, got {:?}", elapsed, res));
let mut runtime = Runtime::new().unwrap();
runtime.block_on(f).unwrap();
}
Shepmaster 的回答很棒。但是,他们使用的 futures
版本已经过时并且与 stdlib futures 不兼容。这是我使用 stdlib 期货的重写。
use pin_project::pin_project;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
use tokio::time; // 0.2
/// A wrapper around a Future which adds timing data.
#[pin_project]
struct Timed<Fut, F>
where
Fut: Future,
F: FnMut(&Fut::Output, Duration),
{
#[pin]
inner: Fut,
f: F,
start: Option<Instant>,
}
impl<Fut, F> Future for Timed<Fut, F>
where
Fut: Future,
F: FnMut(&Fut::Output, Duration),
{
type Output = Fut::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.project();
let start = this.start.get_or_insert_with(Instant::now);
match this.inner.poll(cx) {
// If the inner future is still pending, this wrapper is still pending.
Poll::Pending => Poll::Pending,
// If the inner future is done, measure the elapsed time and finish this wrapper future.
Poll::Ready(v) => {
let elapsed = start.elapsed();
(this.f)(&v, elapsed);
Poll::Ready(v)
}
}
}
}
trait TimedExt: Sized + Future {
fn timed<F>(self, f: F) -> Timed<Self, F>
where
F: FnMut(&Self::Output, Duration),
{
Timed {
inner: self,
f,
start: None,
}
}
}
// All futures can use the `.timed` method defined above
impl<F: Future> TimedExt for F {}
#[tokio::main]
async fn main() {
let when = Instant::now() + Duration::from_millis(100);
let fut = time::delay_until(when.into())
.timed(|res, elapsed| println!("{:?} elapsed, got {:?}", elapsed, res));
fut.await;
}
我正在和 Tokio 一起做一些 UDP 的事情。
我想记录我的 UDP 探测未来需要花费的时间来解决。我想出了以下函数 time_future()
来包装未来并给我结果和持续时间。这个函数看起来很幼稚,我认为 Rust 有能力更清晰地表达这个概念。
我的工作代码(Playground):
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.11
use std::time::{Duration, Instant};
use futures::future::{lazy, ok};
use futures::Future;
use tokio::runtime::current_thread::Runtime;
use tokio::timer::Delay;
struct TimedFutureResult<T, E> {
elapsed: Duration,
result: Result<T, E>,
}
impl<T, E> TimedFutureResult<T, E> {
pub fn elapsed_ms(&self) -> i64 {
return (self.elapsed.as_secs() * 1000 + (self.elapsed.subsec_nanos() / 1000000) as u64)
as i64;
}
}
fn time_future<F: Future>(f: F) -> impl Future<Item = TimedFutureResult<F::Item, F::Error>> {
lazy(|| {
let start = Instant::now();
f.then(move |result| {
ok::<TimedFutureResult<F::Item, F::Error>, ()>(TimedFutureResult {
elapsed: start.elapsed(),
result: result,
})
})
})
}
fn main() {
let when = Instant::now() + Duration::from_millis(100);
let f = time_future(Delay::new(when)).then(|r| match r {
Ok(r) => {
println!("resolved in {}ms", r.elapsed_ms());
r.result
}
_ => unreachable!(),
});
let mut runtime = Runtime::new().unwrap();
runtime.block_on(f).unwrap();
}
我怎样才能改进它并使其更加地道?我能否以某种方式让界面像 inspect()
或 then()
一样工作?
Delay::new(when)
.timed(|res, elapsed| println!("{}ms!", elapsed))
.and_then(...);
我尝试创建一个 Timed
特征并为 Future
实现它,但我对自己的工作方式一点信心都没有。这些类型真的让我大吃一惊。
我至少找对了树吗?
编写 future 的动作很简单,添加一个可链接的方法与
唯一真正棘手的方面是决定时间何时开始——是在创建未来还是在首次轮询时?
我选择在第一次轮询时使用,因为这似乎更有用:
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.11
use std::time::{Duration, Instant};
use futures::{try_ready, Async, Future, Poll};
use tokio::{runtime::current_thread::Runtime, timer::Delay};
struct Timed<Fut, F>
where
Fut: Future,
F: FnMut(&Fut::Item, Duration),
{
inner: Fut,
f: F,
start: Option<Instant>,
}
impl<Fut, F> Future for Timed<Fut, F>
where
Fut: Future,
F: FnMut(&Fut::Item, Duration),
{
type Item = Fut::Item;
type Error = Fut::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
let start = self.start.get_or_insert_with(Instant::now);
let v = try_ready!(self.inner.poll());
let elapsed = start.elapsed();
(self.f)(&v, elapsed);
Ok(Async::Ready(v))
}
}
trait TimedExt: Sized + Future {
fn timed<F>(self, f: F) -> Timed<Self, F>
where
F: FnMut(&Self::Item, Duration),
{
Timed {
inner: self,
f,
start: None,
}
}
}
impl<F: Future> TimedExt for F {}
fn main() {
let when = Instant::now() + Duration::from_millis(100);
let f = Delay::new(when).timed(|res, elapsed| println!("{:?} elapsed, got {:?}", elapsed, res));
let mut runtime = Runtime::new().unwrap();
runtime.block_on(f).unwrap();
}
Shepmaster 的回答很棒。但是,他们使用的 futures
版本已经过时并且与 stdlib futures 不兼容。这是我使用 stdlib 期货的重写。
use pin_project::pin_project;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
use tokio::time; // 0.2
/// A wrapper around a Future which adds timing data.
#[pin_project]
struct Timed<Fut, F>
where
Fut: Future,
F: FnMut(&Fut::Output, Duration),
{
#[pin]
inner: Fut,
f: F,
start: Option<Instant>,
}
impl<Fut, F> Future for Timed<Fut, F>
where
Fut: Future,
F: FnMut(&Fut::Output, Duration),
{
type Output = Fut::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.project();
let start = this.start.get_or_insert_with(Instant::now);
match this.inner.poll(cx) {
// If the inner future is still pending, this wrapper is still pending.
Poll::Pending => Poll::Pending,
// If the inner future is done, measure the elapsed time and finish this wrapper future.
Poll::Ready(v) => {
let elapsed = start.elapsed();
(this.f)(&v, elapsed);
Poll::Ready(v)
}
}
}
}
trait TimedExt: Sized + Future {
fn timed<F>(self, f: F) -> Timed<Self, F>
where
F: FnMut(&Self::Output, Duration),
{
Timed {
inner: self,
f,
start: None,
}
}
}
// All futures can use the `.timed` method defined above
impl<F: Future> TimedExt for F {}
#[tokio::main]
async fn main() {
let when = Instant::now() + Duration::from_millis(100);
let fut = time::delay_until(when.into())
.timed(|res, elapsed| println!("{:?} elapsed, got {:?}", elapsed, res));
fut.await;
}