Ajoutons une autre réponse. Pourquoi cela par rapport aux autres?
1) Simplicité. Essayer de garantir la taille est bien, mais conduit à une complexité inutile qui peut présenter ses propres problèmes.
2) Implémente IReadOnlyCollection, ce qui signifie que vous pouvez utiliser Linq dessus et le transmettre à une variété de choses qui attendent IEnumerable.
3) Pas de verrouillage. La plupart des solutions ci-dessus utilisent des verrous, ce qui est incorrect sur une collection sans verrou.
4) Implémente le même ensemble de méthodes, propriétés et interfaces que ConcurrentQueue, y compris IProducerConsumerCollection, ce qui est important si vous souhaitez utiliser la collection avec BlockingCollection.
Cette implémentation pourrait potentiellement aboutir à plus d'entrées que prévu si TryDequeue échoue, mais la fréquence de ce qui se produit ne semble pas valoir un code spécialisé qui entravera inévitablement les performances et causera ses propres problèmes inattendus.
Si vous voulez absolument garantir une taille, implémenter une méthode Prune () ou similaire semble être la meilleure idée. Vous pouvez utiliser un verrou de lecture ReaderWriterLockSlim dans les autres méthodes (y compris TryDequeue) et prendre un verrou en écriture uniquement lors de l'élagage.
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();
}