Додамо ще одну відповідь. Чому це над іншими?
1) Простота. Намагання гарантувати розмір - це добре і добре, але призводить до зайвої складності, яка може виявляти власні проблеми.
2) Реалізує IReadOnlyCollection, тобто ви можете використовувати Linq на ньому та передавати їх у різноманітні речі, які очікують на IEnumerable.
3) Без блокування. У багатьох рішеннях, описаних вище, використовуються замки, що невірно в безблочній колекції.
4) Реалізує той самий набір методів, властивостей та інтерфейсів, які робить ConcurrentQueue, включаючи IProducerConsumerCollection, що важливо, якщо ви хочете використовувати колекцію з BlockingCollection.
Ця реалізація може потенційно закінчитися більшою кількістю записів, ніж очікувалося, якщо TryDequeue не вдасться, але частота цих випадків не здається спеціальним кодом, який неминуче утруднятиме продуктивність та спричинятиме власні несподівані проблеми.
Якщо ви абсолютно хочете гарантувати розмір, реалізація методу Prune () або подібного методу здається найкращою ідеєю. Ви можете використовувати блокування читання ReaderWriterLockSlim в інших методах (включаючи TryDequeue) та брати блокування запису лише під час обрізки.
class ConcurrentFixedSizeQueue<T> : IProducerConsumerCollection<T>, IReadOnlyCollection<T>, ICollection {
readonly ConcurrentQueue<T> m_concurrentQueue;
readonly int m_maxSize;
public int Count => m_concurrentQueue.Count;
public bool IsEmpty => m_concurrentQueue.IsEmpty;
public ConcurrentFixedSizeQueue (int maxSize) : this(Array.Empty<T>(), maxSize) { }
public ConcurrentFixedSizeQueue (IEnumerable<T> initialCollection, int maxSize) {
if (initialCollection == null) {
throw new ArgumentNullException(nameof(initialCollection));
}
m_concurrentQueue = new ConcurrentQueue<T>(initialCollection);
m_maxSize = maxSize;
}
public void Enqueue (T item) {
m_concurrentQueue.Enqueue(item);
if (m_concurrentQueue.Count > m_maxSize) {
T result;
m_concurrentQueue.TryDequeue(out result);
}
}
public void TryPeek (out T result) => m_concurrentQueue.TryPeek(out result);
public bool TryDequeue (out T result) => m_concurrentQueue.TryDequeue(out result);
public void CopyTo (T[] array, int index) => m_concurrentQueue.CopyTo(array, index);
public T[] ToArray () => m_concurrentQueue.ToArray();
public IEnumerator<T> GetEnumerator () => m_concurrentQueue.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator () => GetEnumerator();
// Explicit ICollection implementations.
void ICollection.CopyTo (Array array, int index) => ((ICollection)m_concurrentQueue).CopyTo(array, index);
object ICollection.SyncRoot => ((ICollection) m_concurrentQueue).SyncRoot;
bool ICollection.IsSynchronized => ((ICollection) m_concurrentQueue).IsSynchronized;
// Explicit IProducerConsumerCollection<T> implementations.
bool IProducerConsumerCollection<T>.TryAdd (T item) => ((IProducerConsumerCollection<T>) m_concurrentQueue).TryAdd(item);
bool IProducerConsumerCollection<T>.TryTake (out T item) => ((IProducerConsumerCollection<T>) m_concurrentQueue).TryTake(out item);
public override int GetHashCode () => m_concurrentQueue.GetHashCode();
public override bool Equals (object obj) => m_concurrentQueue.Equals(obj);
public override string ToString () => m_concurrentQueue.ToString();
}