Dodajmy jeszcze jedną odpowiedź. Dlaczego to ponad innymi?
1) Prostota. Próba zagwarantowania, że rozmiar jest dobry i dobry, ale prowadzi do niepotrzebnej złożoności, która może powodować własne problemy.
2) Implementuje IReadOnlyCollection, co oznacza, że możesz użyć Linq na nim i przekazać go do różnych rzeczy, które oczekują IEnumerable.
3) Bez blokowania. Wiele z powyższych rozwiązań wykorzystuje zamki, co jest nieprawidłowe w przypadku kolekcji bez zamków.
4) Implementuje ten sam zestaw metod, właściwości i interfejsów, co ConcurrentQueue, w tym IProducerConsumerCollection, co jest ważne, jeśli chcesz używać kolekcji z BlockingCollection.
Ta implementacja może potencjalnie zakończyć się większą liczbą wpisów niż oczekiwano, jeśli TryDequeue zawiedzie, ale częstotliwość tego nie wydaje się warta wyspecjalizowanego kodu, który nieuchronnie obniży wydajność i spowoduje własne nieoczekiwane problemy.
Jeśli absolutnie chcesz zagwarantować rozmiar, zaimplementowanie metody Prune () lub podobnej wydaje się najlepszym pomysłem. Możesz użyć blokady odczytu ReaderWriterLockSlim w innych metodach (w tym TryDequeue) i przyjąć blokadę zapisu tylko podczas czyszczenia.
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();
}