防止`chan :: Receiver`在空缓冲区上阻塞

时间:2017-10-22 07:26:28

标签: multithreading rust channel rust-tokio

我想构建一个多生产者多用户(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()
}

1 个答案:

答案 0 :(得分:1)

chan crate提供chan_select宏,允许非阻塞recv;但要为这些原语实现Future,您还需要在通道准备就绪时唤醒任务(请参阅futures::task::current())。

您可以使用现有基元实现Future;实施新的通常更加困难。在这种情况下,您可能需要分叉chan以使其Future兼容。

似乎multiqueue crate有一个Future兼容的mpmc频道mpmc_fut_queue