BlockingCollection最大尺寸

时间:2013-04-10 14:26:49

标签: c# .net-4.0 concurrency

据我所知,使用ConcurrentQueue的BlockingCollection的有效容量为100。

但是我不确定这意味着什么。

我正在尝试实现一个并发缓存,如果队列大小太大,可以在一个操作中出列,可以deque / enque(即缓存溢出时松散的消息)。有没有办法为此使用boundedcapacity,或者手动执行此操作或创建新集合更好。

基本上我有一个阅读线程和几个写线程。如果队列中的数据是所有作者中“最新鲜的”,我想要它。

3 个答案:

答案 0 :(得分:2)

听起来我正在尝试构建类似MRU(最近使用的)缓存的东西。 BlockingCollection不是最好的方法。

我建议您使用LinkedList。它不是线程安全的,所以你必须提供自己的同步,但这并不是太难。你的入队方法如下:

LinkedList<MyType> TheQueue = new LinkedList<MyType>();
object listLock = new object();

void Enqueue(MyType item)
{
    lock (listLock)
    {
        TheQueue.AddFirst(item);
        while (TheQueue.Count > MaxQueueSize)
        {
            // Queue overflow. Reduce to max size.
            TheQueue.RemoveLast();
        }
    }
}

出队更容易:

MyType Dequeue()
{
    lock (listLock)
    {
        return (TheQueue.Count > 0) ? TheQueue.RemoveLast() : null;
    }
}

如果您希望消费者在队列中进行非繁忙等待,则需要更多参与。您可以使用Monitor.WaitMonitor.Pulse来完成此操作。有关示例,请参阅Monitor.Pulse页面上的示例。

更新

我觉得你可以用循环缓冲区(数组)做同样的事情。只需保持头尾指针。您在head处插入并在tail处删除。如果您要插入head == tail,那么您需要增加tail,这有效地删除了之前的tail项。

答案 1 :(得分:1)

N的有界容量意味着如果队列已经包含N个项目,则任何尝试添加另一个项目的线程都将阻塞,直到另一个线程删除一个项目。

您似乎想要的是一个不同的概念 - 您希望最近添加的项目是消费线程出列的第一个项目。

您可以通过为基础商店使用ConcurrentStack而不是ConcurrentQueue来实现这一点。

您可以使用this constructor并传入ConcurrentStack

例如:

var blockingCollection = new BlockingCollection<int>(new ConcurrentStack<int>());

通过使用ConcurrentStack,您可以确保消费线程出列的每个项目都是当时队列中最新鲜的项目。

另请注意,如果您为阻止集合指定了上限,则可以使用BlockingCollection.TryAdd(),如果在您调用该集合时该集合已满,则会返回false

答案 2 :(得分:1)

如果您想要一个自定义 BlockingCollection 来保存 N 个最近的元素,并在它满时删除最旧的元素,您可以很容易地基于 Channel<T> 创建一个。 Channels 旨在用于异步场景,但让它们阻塞消费者是微不足道的,并且不应导致任何不需要的副作用(如死锁),即使在具有 SynchronizationContext 的环境中使用已安装。

public class MostRecentBlockingCollection<T>
{
    private readonly Channel<T> _channel;

    public MostRecentBlockingCollection(int capacity)
    {
        _channel = Channel.CreateBounded<T>(new BoundedChannelOptions(capacity)
        {
            FullMode = BoundedChannelFullMode.DropOldest,
        });
    }

    public bool IsCompleted => _channel.Reader.Completion.IsCompleted;

    public void Add(T item)
        => _channel.Writer.WriteAsync(item).AsTask().GetAwaiter().GetResult();

    public T Take()
        => _channel.Reader.ReadAsync().AsTask().GetAwaiter().GetResult();

    public void CompleteAdding() => _channel.Writer.Complete();

    public IEnumerable<T> GetConsumingEnumerable()
    {
        while (_channel.Reader.WaitToReadAsync().AsTask().GetAwaiter().GetResult())
            while (_channel.Reader.TryRead(out var item))
                yield return item;
    }
}

MostRecentBlockingCollection 类仅阻止消费者。生产者总是可以在集合中添加项目,从而(可能)导致一些先前添加的元素被删除。

添加取消支持应该很简单,因为 Channel<T> API 已经支持它。添加对超时的支持不是那么简单,但应该不会很难做到。