我希望能够按照以下步骤做一些事情,以便异步关闭Receiver
流:
extern crate futures;
extern crate tokio;
use futures::future::lazy;
use futures::stream::AndThen;
use futures::sync::mpsc::Receiver;
use futures::{Future, Sink, Stream};
use std::sync::{Arc, Mutex};
use tokio::timer::{Delay, Interval};
fn main() {
tokio::run(lazy(|| {
let (tx, rx) = futures::sync::mpsc::channel(1000);
let arc = Arc::new(Mutex::<Option<AndThen<Receiver<u32>, _, _>>>::new(None));
{
let mut and_then = arc.lock().unwrap();
*and_then = Some(rx.and_then(|num| {
println!("{}", num);
Ok(())
}));
}
let arc_clone = arc.clone();
// This is the part I'd like to be able to do
// After one second, close the `Receiver` so that future
// calls to the `Sender` don't call the callback above in the
// closure passed to `rx.and_then`
tokio::spawn(
Delay::new(std::time::Instant::now() + std::time::Duration::from_secs(1))
.map_err(|e| eprintln!("Some delay err {:?}", e))
.and_then(move |_| {
let mut maybe_stream = arc_clone.lock().unwrap();
match maybe_stream.take() {
Some(stream) => stream.into_inner().close(),
None => eprintln!("Can't close non-existent stream"), // line "A"
}
Ok(())
}),
);
{
let mut maybe_stream = arc.lock().unwrap();
let stream = maybe_stream.take().expect("Stream already ripped out"); // line "B"
let rx = stream.for_each(|_| Ok(()));
tokio::spawn(rx);
}
tokio::spawn(
Interval::new_interval(std::time::Duration::from_millis(10))
.take(10)
.map_err(|e| {
eprintln!("Interval error?! {:?}", e);
})
.fold((tx, 0), |(tx, i), _| {
tx.send(i as u32)
.map_err(|e| eprintln!("Send error?! {:?}", e))
.map(move |tx| (tx, i + 1))
})
.map(|_| ()),
);
Ok(())
}));
}
但是,行A运行是因为我必须在行B上移动流才能在其上调用.for_each
。如果我不打.for_each
(或类似的电话),就我所知,我根本无法执行AndThen
。我不能在不实际移动对象的情况下调用.for_each
,因为for_each
是一种移动方法。
我可以做我想做的事吗?看来这绝对应该是可能的,但也许我缺少明显的东西。
我在使用0.1的期货,在0.1的东京。
答案 0 :(得分:2)
不要说谎,我和@shepmaster在一起,您的问题还不清楚。就是说,感觉就像您正在尝试做mpsc
的{{1}}部分不适合做的事情。
无论如何。解释时间。
每当您组合/组合流(或期货!)时,每种组合方法都采用futures
,而不是我想您希望的self
或&self
。
进入您的代码块的那一刻:
&mut self
...当您 {
let mut maybe_stream = arc.lock().unwrap();
let stream = maybe_stream.take().expect("Stream already ripped out"); // line "B"
let rx = stream.for_each(|_| Ok(()));
tokio::spawn(rx);
}
从Arc<Option<Receiver<T>>>
中提取流时,其内容被take()
替换。然后,将其生成到Tokio反应堆上,该反应堆开始处理此部分。该None
现在处于循环状态,不再对您可用。此外,您的rx
现在包含maybe_stream
。
在延迟之后,您然后尝试None
的内容take()
(A行)。由于现在什么都没有了,因此您一无所有,因此也没有什么可以关闭的了。您的代码错误了。
使用一种机制来停止流本身,而不是绕过Arc<Option<Receiver<T>>>
并希望销毁它。您可以自己这样做,也可以使用mpsc::Receiver
之类的板条箱为您这样做。
DIY版本在这里,可通过您的代码进行修改:
stream-cancel
添加的extern crate futures;
extern crate tokio;
use futures::future::lazy;
use futures::{future, Future, Sink, Stream};
use std::sync::{Arc, RwLock};
use std::sync::atomic::{Ordering, AtomicBool};
use tokio::timer::{Delay, Interval};
fn main() {
tokio::run(lazy(|| {
let (tx, rx) = futures::sync::mpsc::channel(1000);
let circuit_breaker:Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
let c_b_copy = Arc::clone(&circuit_breaker);
tokio::spawn(
Delay::new(std::time::Instant::now() + std::time::Duration::from_secs(1))
.map_err(|e| eprintln!("Some delay err {:?}", e))
.and_then(move |_| {
// We set the CB to true in order to stop processing of the stream
circuit_breaker.store(true, Ordering::Relaxed);
Ok(())
}),
);
{
let rx2 = rx.for_each(|e| {
println!("{:?}", e);
Ok(())
});
tokio::spawn(rx2);
}
tokio::spawn(
Interval::new_interval(std::time::Duration::from_millis(100))
.take(100)
// take_while causes the stream to continue as long as its argument returns a future resolving to true.
// In this case, we're checking every time if the circuit-breaker we've introduced is false
.take_while(move |_| {
future::ok(
c_b_copy.load(Ordering::Relaxed) == false
);
})
.map_err(|e| {
eprintln!("Interval error?! {:?}", e);
})
.fold((tx, 0), |(tx, i), _| {
tx.send(i as u32)
.map_err(|e| eprintln!("Send error?! {:?}", e))
.map(move |tx| (tx, i + 1))
})
.map(|_| ()),
);
Ok(())
}));
}
允许您对流的内容或外部谓词进行操作,以继续或停止流。请注意,即使我们使用的是take_while()
,由于Tokio的AtomicBool
生命周期要求,我们仍然需要Arc
。
在评论中进行一些讨论之后,this solution可能更适合您的用例。我有效地实现了断路器覆盖的扇出流。魔术发生在这里:
'static
如果状态指示器设置为false,则对上面的流进行轮询;然后将结果发送给所有侦听器。如果impl<S> Stream for FanOut<S> where S:Stream, S::Item:Clone {
type Item = S::Item;
type Error = S::Error;
fn poll(&mut self) -> Result<Async<Option<S::Item>>, S::Error> {
match self.inner.as_mut() {
Some(ref mut r) => {
let mut breaker = self.breaker.write().expect("Poisoned lock");
match breaker.status {
false => {
let item = r.poll();
match &item {
&Ok(Async::Ready(Some(ref i))) => {
breaker.registry.iter_mut().for_each(|sender| {
sender.try_send(i.clone()).expect("Dead channel");
});
item
},
_ => item
}
},
true => Ok(Async::Ready(None))
}
}
_ => {
let mut breaker = self.breaker.write().expect("Poisoned lock");
// Stream is over, drop all the senders
breaker.registry = vec![];
Ok(Async::Ready(None))
}
}
}
}
的结果为poll
(表明流已完成),则关闭所有侦听器通道。
如果状态指示器设置为true,则关闭所有侦听器通道,并且流返回Async::Ready(None)
(并由Tokio丢弃)。
Async::Ready(None)
对象是可克隆的,但是只有初始实例才能执行任何操作。
答案 1 :(得分:1)
您可以使用stream-cancel之类的板条箱来实现此目的。在这里,我使用了Valved
流包装器,该包装器获取现有流并返回一个值,以后可用来取消该流:
use futures::{
future::lazy,
{Future, Sink, Stream},
}; // 0.1.25
use stream_cancel::Valved; // 0.4.4
use tokio::timer::{Delay, Interval}; // 0.1.13
fn main() {
tokio::run(lazy(|| {
let (tx, rx) = futures::sync::mpsc::channel(1000);
let (trigger, rx) = Valved::new(rx);
tokio::spawn({
rx.for_each(|num| {
println!("{}", num);
Ok(())
})
});
tokio::spawn({
Delay::new(std::time::Instant::now() + std::time::Duration::from_secs(1))
.map_err(|e| eprintln!("Some delay err {:?}", e))
.map(move |_| trigger.cancel()),
});
tokio::spawn({
Interval::new_interval(std::time::Duration::from_millis(10))
.take(10)
.map_err(|e| eprintln!("Interval error?! {:?}", e))
.fold((tx, 0), |(tx, i), _| {
tx.send(i)
.map_err(|e| eprintln!("Send error?! {:?}", e))
.map(move |tx| (tx, i + 1))
})
.map(|_| ()),
});
Ok(())
}));
}
板条箱还具有其他类型,可用于不同的用例,请务必查看文档。
请参阅Sébastien Renauld's answer,了解一种自己实现此方法的方法。