具有上限的线程安全集合 [英] thread safe Collection with upper bound

查看:68
本文介绍了具有上限的线程安全集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在进行具有以下属性的收藏:

I am after a collection with the following properties:

  • 线程安全:它将在asp.net中使用,并且多个客户端可以尝试同时添加,删除和访问成员
  • 最大元素:我希望能够在构造时设置上限,最大元素数量
  • TryAdd :与BlockingCollection<T>.TryAdd(T)相同的方法将是完美的,即,如果达到最大元素数,则将返回false
  • 类似字典的:在大多数其他方面,ConcurrentDictionary是完美的,即能够通过键识别元素的能力,删除任何项目(不仅是第一个或最后一个,我认为这会是BlockingCollection的限制)
  • threadsafe: it will be used in asp.net and multiple clients could try to add, remove and access members concurrently
  • max elements: I want to be able to set an upper bound, a maximum number of elements, at construction time
  • TryAdd: a method that works the same as BlockingCollection<T>.TryAdd(T) would be perfect, i.e. it would return false if the maximum number of elements has been reached
  • Dictionary-like: In most other respects a ConcurrentDictionary would be perfect, i.e. ability to identify elements by a key, remove any item (not just the first or last, which I think would be the limitation with BlockingCollection)

在我尝试自己动手之前,我的问题是:

Before I attempt to roll my own, my questions are:

  1. 我是否错过了一个内置类型,该类型会为集合中的元素数量设置一个安全的上限?
  2. 是否可以通过某种方式使用BlockingCollection实现此功能?
  1. have I missed a built in type that would put a safe ceiling on the number of elements in a collection?
  2. Is there a way to achieve this functionality with BlockingCollection somehow?

最后,如果我需要尝试自己动手制作,我应该考虑哪种方法?它像用locks包裹的Dictionary一样简单吗?

Finally, if I do need to try and make my own, what approach should I think about? Is it as simple as a wrapped Dictionary with locks?

用法示例: 定义了参加人数限制的聊天室可以存储参加者的联系信息,并拒绝新进入者,直到满员时才有进入的空间

Example use: A chat room with a defined limit on number of participants could store the connection information of participants and reject new entrants until there is room to enter when full

推荐答案

最简单的解决方案是制作一个包装类,该包装类使用常规词典并使用ReaderWriterLockSlim来控制线程安全访问.

The simplest solution is just make a wrapper class that uses a normal dictionary and uses a ReaderWriterLockSlim to control thread safe access.

public class SizeLimitedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
    private readonly int _maxSize;
    private readonly IDictionary<TKey, TValue> _dictionary;
    private readonly ReaderWriterLockSlim _readerWriterLock;

    public SizeLimitedDictionary(int maxSize)
    {
        _maxSize = maxSize;
        _dictionary = new Dictionary<TKey, TValue>(_maxSize);
        _readerWriterLock = new ReaderWriterLockSlim();
    }

    public bool TryAdd(TKey key, TValue value)
    {
        _readerWriterLock.EnterWriteLock();
        try
        {
            if (_dictionary.Count >= _maxSize)
                return false;

            _dictionary.Add(key, value);
        }
        finally
        {
            _readerWriterLock.ExitWriteLock();
        }

        return true;
    }

    public void Add(TKey key, TValue value)
    {
        bool added = TryAdd(key, value);
        if(!added)
            throw new InvalidOperationException("Dictionary is at max size, can not add additional members.");
    }

    public bool TryAdd(KeyValuePair<TKey, TValue> item)
    {
        _readerWriterLock.EnterWriteLock();
        try
        {
            if (_dictionary.Count >= _maxSize)
                return false;

            _dictionary.Add(item);
        }
        finally
        {
            _readerWriterLock.ExitWriteLock();
        }

        return true;
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        bool added = TryAdd(item);
        if (!added)
            throw new InvalidOperationException("Dictionary is at max size, can not add additional members.");
    }

    public void Clear()
    {
        _readerWriterLock.EnterWriteLock();
        try
        {
            _dictionary.Clear();
        }
        finally
        {
            _readerWriterLock.ExitWriteLock();
        }

    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        _readerWriterLock.EnterReadLock();
        try
        {
            return _dictionary.Contains(item);
        }
        finally
        {
            _readerWriterLock.ExitReadLock();
        }

    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        _readerWriterLock.EnterReadLock();
        try
        {
            _dictionary.CopyTo(array, arrayIndex);
        }
        finally
        {
            _readerWriterLock.ExitReadLock();
        }
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        _readerWriterLock.EnterWriteLock();
        try
        {
            return _dictionary.Remove(item);
        }
        finally
        {
            _readerWriterLock.ExitWriteLock();
        }
    }

    public int Count
    {
        get
        {
            _readerWriterLock.EnterReadLock();
            try
            {
                return _dictionary.Count;
            }
            finally
            {
                _readerWriterLock.ExitReadLock();
            }
        }
    }

    public bool IsReadOnly
    {
        get
        {
            _readerWriterLock.EnterReadLock();
            try
            {
                return _dictionary.IsReadOnly;
            }
            finally
            {
                _readerWriterLock.ExitReadLock();
            }
        }
    }

    public bool ContainsKey(TKey key)
    {
        _readerWriterLock.EnterReadLock();
        try
        {
            return _dictionary.ContainsKey(key);
        }
        finally
        {
            _readerWriterLock.ExitReadLock();
        }
    }

    public bool Remove(TKey key)
    {
        _readerWriterLock.EnterWriteLock();
        try
        {
            return _dictionary.Remove(key);
        }
        finally
        {
            _readerWriterLock.ExitWriteLock();
        }
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        _readerWriterLock.EnterReadLock();
        try
        {
            return _dictionary.TryGetValue(key, out value);
        }
        finally
        {
            _readerWriterLock.ExitReadLock();
        }
    }

    public TValue this[TKey key]
    {
        get
        {
            _readerWriterLock.EnterReadLock();
            try
            {
                return _dictionary[key];
            }
            finally
            {
                _readerWriterLock.ExitReadLock();
            }
        }
        set
        {
            _readerWriterLock.EnterUpgradeableReadLock();
            try
            {
                var containsKey = _dictionary.ContainsKey(key);
                _readerWriterLock.EnterWriteLock();
                try
                {
                    if (containsKey)
                    {
                        _dictionary[key] = value;
                    }
                    else
                    {
                        var added = TryAdd(key, value);
                        if(!added)
                            throw new InvalidOperationException("Dictionary is at max size, can not add additional members.");
                    }
                }
                finally
                {
                    _readerWriterLock.ExitWriteLock();
                }
            }
            finally
            {
                _readerWriterLock.ExitUpgradeableReadLock();
            }
        }
    }

    public ICollection<TKey> Keys
    {
        get
        {
            _readerWriterLock.EnterReadLock();
            try
            {
                return _dictionary.Keys;
            }
            finally
            {
                _readerWriterLock.ExitReadLock();
            }
        }
    }

    public ICollection<TValue> Values
    {
        get
        {
            _readerWriterLock.EnterReadLock();
            try
            {
                return _dictionary.Values;
            }
            finally
            {
                _readerWriterLock.ExitReadLock();
            }
        }
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return _dictionary.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable)_dictionary).GetEnumerator();
    }
}

此类实现完整的IDictionary<Tkey,TValue>接口.这种工作方式是所有插入都通过TryAdd,如果您等于或大于最大大小,并尝试插入新成员,则会从TryAdd获得false,而从不返回的方法获得InvalidOperationException bool.

This class implements the full IDictionary<Tkey,TValue> interface. The way this works is all insertions pass through TryAdd, if you are at or above the max size and try to insert a new member you get a false from TryAdd and a InvalidOperationException from methods that do not return bool.

我不使用ConcurrentDictionary的原因是没有很好的方法来尝试检查计数,然后再在

The reason I did not use a ConcurrentDictionary is there is no good way to try to check the count before adding a new member in an atomic way, so you would need to lock anyway. You could potentially use a concurrent dictionary and remove all of my EnterReadLock's and replace the EnterWriteLock's with normal lock calls, but you would need to do performance testing to see which would do better.

如果您想要类似GetOrAdd的方法,那么实现自己并不难.

If you want methods like GetOrAdd it would not be hard to implement yourself.

这篇关于具有上限的线程安全集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆