据我所知,使用ConcurrentQueue的BlockingCollection的有效容量为100。
但是我不确定这意味着什么。
我正在尝试实现一个并发缓存,如果队列大小太大,可以在一个操作中出列,可以deque / enque(即缓存溢出时松散的消息)。有没有办法为此使用boundedcapacity,或者手动执行此操作或创建新集合更好。
基本上我有一个阅读线程和几个写线程。如果队列中的数据是所有作者中“最新鲜的”,我想要它。
答案 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.Wait
和Monitor.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 已经支持它。添加对超时的支持不是那么简单,但应该不会很难做到。