我想构建一个多生产者多用户(MPMC)通道,其中包含不同的并发任务,并在其中生成数据。其中一些任务负责与文件系统或网络连接。
两个例子:
PrintOutput(String)
将由记录器,控制台输出或GUI使用。
NewJson(String)
将由记录器或解析器使用。
为实现这一目标,我选择chan
作为MPMC频道提供者,选择tokio
作为系统来管理频道上每个听众的事件循环。
在阅读tokio's site上的示例后,我开始为futures::stream::Stream
实施chan::Receiver
。这将允许每个未来使用a来收听频道。但是,这两个库的文档突出了冲突:
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error>
尝试提取此流的下一个值,如果流已完成则返回None。
此方法与Future :: poll一样,是从流中提取值的唯一方法。此方法通常也必须在任务的上下文中运行,并且此特征的实现者必须确保此方法的实现不会阻止,因为它可能会导致消费者行为不当。
当缓冲区为空时,
fn recv(&self) -> Option<T>
在此频道上接收值。
如果这是一个非同步通道,则当缓冲区为空时,仅重新阻止。
如果这是同步通道,则recv仅在缓冲区为空时阻塞。
如果这是一个会合通道,则recv会阻塞,直到相应的发送值发送。
对于所有通道,如果通道关闭且缓冲区为空,则recv始终立即返回None。 (如果缓冲区在封闭通道上非空,则返回缓冲区中的值。)
保证以与发送顺序相同的顺序接收值。
此操作永远不会恐慌!但如果频道从未关闭,它可能会死锁。
chan::Receiver
可能会阻塞,但futures::stream::Stream
期望在轮询时永不阻塞。
如果空缓冲区阻塞,则无法确认它是否为空。如何检查缓冲区是否为空以防止阻塞?
尽管Kabuki在我的雷达上并且似乎是最成熟的演员模型箱,但它几乎完全没有文档。
这是我到目前为止的实施:
extern crate chan;
extern crate futures;
struct RX<T>(chan::Receiver<T>);
impl<T> futures::stream::Stream for RX<T> {
type Item = T;
type Error = Box<std::error::Error>;
fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
let &mut RX(ref receiver) = self;
let item = receiver.recv();
match item {
Some(value) => Ok(futures::Async::Ready(Some(value))),
None => Ok(futures::Async::NotReady),
}
}
}
我已经完成了快速测试,看看它是如何工作的。看起来没问题,但正如预期的那样,在完成缓冲后会阻塞。虽然这应该有效,但我有点担心消费者“表现得很糟糕”的含义。现在我将继续测试这种方法,希望我不会遇到不良行为。
extern crate chan;
extern crate futures;
use futures::{Stream, Future};
fn my_test() {
let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
let (tx, rx) = chan::async::<String>();
tx.send("Hello".to_string()); // fill the buffer before it blocks; single thread here.
let incoming = RX(rx).for_each(|s| {
println!("Result: {}", s);
Ok(())
});
core.run(incoming).unwrap()
}
答案 0 :(得分:1)
chan
crate提供chan_select
宏,允许非阻塞recv
;但要为这些原语实现Future
,您还需要在通道准备就绪时唤醒任务(请参阅futures::task::current()
)。
您可以使用现有基元实现Future
;实施新的通常更加困难。在这种情况下,您可能需要分叉chan
以使其Future
兼容。
似乎multiqueue
crate有一个Future
兼容的mpmc频道mpmc_fut_queue
。