【问题标题】:C# fixed size ConcurrentQueue get element at indexC# 固定大小的 ConcurrentQueue 获取索引处的元素
【发布时间】:2019-01-18 22:03:43
【问题描述】:

我发现了一个实现线程安全队列的旧代码。 我尝试使用 ConcurrentQueue 创建一个新的实现

旧代码

public class BoundedQueue<T> where T : class
    {
        private readonly Queue<T> _fixedSizeBuffer;
        private object _bufferLock;
        private int _limit;

        public BoundedQueue(int limit)
        {
            _fixedSizeBuffer = new Queue<T>();
            _limit = limit;
            _bufferLock = new object();

            for (int i = 0; i < _limit; i++)
            {
                _fixedSizeBuffer.Enqueue(null);
            }
        }

        public void AddElementToBuffer(T element)
        {
            lock (_bufferLock)
            {
                _fixedSizeBuffer.Enqueue(element);
                while (_fixedSizeBuffer.Count > _limit)
                {
                    _fixedSizeBuffer.Dequeue();
                }
            }
        }

        public T GetElementAt(int index)
        {
            T element;
            lock (_bufferLock)
            {
                element = _fixedSizeBuffer.ElementAt(_limit - index - 1);
            }
            return element;
        }
    }

我的新代码

public class FixedSizeConcurrentQueue<T> where T : class
{
    private readonly ConcurrentQueue<T> _fixedSizeBuffer;
    private int _maxSize;
    public FixedSizeConcurrentQueue(int maxSize)
    {
        _maxSize = maxSize;
        for (int i = 0; i < _maxSize; i++)
        {
            _fixedSizeBuffer.Enqueue(null);
        }
    }

    public void AddElementToBuffer(T element)
    {
        _fixedSizeBuffer.Enqueue(element);
        while (_fixedSizeBuffer.Count > _maxSize)
        {
            T item;
            _fixedSizeBuffer.TryDequeue(out item);
        }
    }
    public T GetElementAt(int index)
    {
        var element = _fixedSizeBuffer.ElementAt(_maxSize - index - 1);
        return element;
    }
}

我的问题是关于ElementAt() 函数,或者我应该更好地将其称为TryGetElement()
在旧代码中,代码使用锁来同步不同的线程。
但是在新代码中我删除了它,因为我知道在并发收集时使用锁是一种不好的做法。
因此,如果由于队列为空而找不到索引,例如,我会遇到异常。
那么我应该用 try catch 包装它吗?
请解释一下你会怎么做。

【问题讨论】:

    标签: c# concurrency queue thread-safety


    【解决方案1】:

    您的maxsize 变量未跨线程同步,因此存在线程问题。此外,ConcurrentQueue 已经有一个 ElementAtOrDefault 函数,如果索引不存在,它将自动返回 null

    我会更新为继承自 ConcurrentQueue 本身。

    public class FixedSizeConcurrentQueue<T> : ConcurrentQueue<T>
    {
        public int MaxSize { get; }
    
        public FixedSizeConcurrentQueue(int maxSize)
        {
            MaxSize = maxSize;
        }
    
        public new void Enqueue(T obj)
        {
            base.Enqueue(obj);
    
            while (base.Count > MaxSize)
            {
                T outObj;
                base.TryDequeue(out outObj);
            }
        }
    
        public T GetElementAt(int index)
        {
            return base.ElementAtOrDefault(index);;
        }
    }
    

    【讨论】:

    • 出队时无需锁定,有一篇关于此的完整帖子。但是感谢 elementOrDefault 我确实知道那个
    • @Gilad 对,出队时不必锁定,但需要锁定'Size',这不是线程安全的。
    • 所以你可以使用 Interlocked 类来解决这个问题?
    • @Gilad 是的,你可以。我更新了答案,只使用 Size get 上的锁。
    • @d.moncada 为什么你的吸气剂中有锁?这是一个只读结构......也不热衷于隐藏“新”方法。如果你这样做 ConcurrentQueue queue = new FixedSizeConcurrentQueue 你的队列将不会被调用!
    猜你喜欢
    • 1970-01-01
    • 2021-08-15
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多