ขอเพิ่มอีกหนึ่งคำตอบ ทำไมถึงเป็นเช่นนี้?
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();
}