【问题标题】:Does the IsCompleted property in the BlockingCollection<T> actually block?BlockingCollection<T> 中的 IsCompleted 属性是否实际阻塞?
【发布时间】:2017-08-23 22:10:09
【问题描述】:

我正在尝试为我从控制器异步读取的数据流实现生产者/消费者模式。我想使用BlockingCollection&lt;T&gt; 这样做,但想确保我得到想要的结果。我的消费者看起来像这样:

// make sure there is actually some data in the buffer
if (!this.buffer.IsCompleted)
{
    // attempt to read from the buffer
    data = this.buffer.Take();

    // construct the message object
    message = this.ConvertToMessageObject(data);
}

IsCompleted 属性实际上会阻塞吗?因此,如果另一个线程要访问缓冲区,我希望它在调用 Take 方法之前等待并确保缓冲区实际上没有“完成”。

在我的应用程序中,期望的效果是允许我避免在缓冲区实际上为空时构造新的消息对象。所以这就是为什么我在去Take之前检查IsCompleted

另外...我知道Take 方法不再阻塞一次IsAddingCompleted = true。因此,我不希望消费者从无效的 Take 方法中获取数据,因为它别无选择(如果消费者不知道已完成状态)。我认为我很难解释我在这里担心的事情......

【问题讨论】:

    标签: c# blocking producer-consumer


    【解决方案1】:

    IsCompleted 属性实际上会阻塞吗?

    不,它会立即返回。

    因此,如果另一个线程要访问缓冲区,我希望它等待并确保在调用 Take 方法之前缓冲区实际上没有“完成”。

    这样做。当然,如果调用IsCompleted 时缓冲区未完成,它将返回false。它可能在返回该值之后完成,并且在您调用IsCompleted 时队列中可能没有任何项目。

    在我的应用程序中,期望的效果是允许我避免在缓冲区实际上为空时构造新的消息对象。

    Take 如果集合被阻塞并且其中没有项目,则将抛出异常,因此您无需担心。它不会永远阻塞,也不会在没有任何实际数据的情况下构造新消息。完成后,您可以在循环外捕获异常以继续。

    话虽如此,让BlockingCollection 为您处理迭代要比尝试构建您自己的消耗迭代器要容易得多(尽管自己动手并不难) .你可以写:

    foreach(var data in buffer.GetConsumingEnumerable())
        //...
    

    它将消耗序列中的项目,直到缓冲区完成,然后在缓冲区中没有并且永远不会有任何项目时退出循环。

    【讨论】:

    • 我的担忧是...在检查IsCompleted 和调用Take...之间有一个线程处于一种状态,而另一个线程仍然阻塞Take 方法。那里会发生什么?如果缓冲区为空,我不想调用Take,这就是我想要实现的目标。
    • @Snoopy 我确切地提到了我的回答中发生的事情。
    • 我知道,您或多或少会说“在您致电IsCompleted 时,队列中可能没有任何项目”。这是我真正关心的问题,这个问题如何解决?我很困惑。
    • @Snoopy 继续阅读,而不是在答案的前半部分之后停下来。我在下一段(以及后面的一段)中回答了这个确切的问题。
    • 好的,只是想说声谢谢(回答主要问题)。另外,我理解你所说的关于异常的内容。但是,即使您还没有阻塞,也会发生异常吗?就像我直接在IsCompleted 缓冲区上调用Take 一样......那会抛出异常吗?如果是这样的话,那么我所要做的就是确保我的客户端代码足够聪明,不会让消费者这样做。怎么样?
    猜你喜欢
    • 2020-12-16
    • 2011-10-09
    • 1970-01-01
    • 2018-05-01
    • 1970-01-01
    • 2011-04-19
    • 2020-09-16
    • 2011-04-09
    • 2017-01-25
    相关资源
    最近更新 更多