如何在.Net中实现ConcurrentHashSet
我试图在ConcurrentDictionary的精神中实现一个ConcurrentHashSet,采取的方法是使用内部支持ConcurrentDictionary并编写小的委托方法,这是我得到了多远,但是设置理论方法是我坚持,尤其是。 我不确定我是否可以使用foreach并且仍然不违反并发性
public class ConcurrentHashSet : ISet { private readonly ConcurrentDictionary _internal; public ConcurrentHashSet(IEnumerable elements = null) { _internal = new ConcurrentDictionary(); if (elements != null) UnionWith(elements); } public void UnionWith(IEnumerable other) { if (other == null) throw new ArgumentNullException("other"); foreach (var otherElement in other) Add(otherElement); } public void IntersectWith(IEnumerable other) { throw new NotImplementedException(); } public void ExceptWith(IEnumerable other) { throw new NotImplementedException(); } public void SymmetricExceptWith(IEnumerable other) { throw new NotImplementedException(); } public bool IsSubsetOf(IEnumerable other) { throw new NotImplementedException(); } public bool IsSupersetOf(IEnumerable other) { throw new NotImplementedException(); } public bool IsProperSupersetOf(IEnumerable other) { throw new NotImplementedException(); } public bool IsProperSubsetOf(IEnumerable other) { throw new NotImplementedException(); } public bool Overlaps(IEnumerable other) { return other.Any(otherElement => _internal.ContainsKey(otherElement)); } public bool SetEquals(IEnumerable other) { int otherCount = 0; int thisCount = Count; foreach (var otherElement in other) { otherCount++; if (!_internal.ContainsKey(otherElement)) return false; } return otherCount == thisCount; } public bool Add(TElement item) { return _internal.TryAdd(item, null); } public void Clear() { _internal.Clear(); } // I am not sure here if that fullfills contract correctly void ICollection.Add(TElement item) { Add(item); } public bool Contains(TElement item) { return _internal.ContainsKey(item); } public void CopyTo(TElement[] array, int arrayIndex) { _internal.Keys.CopyTo(array, arrayIndex); } public bool Remove(TElement item) { object ignore; return _internal.TryRemove(item, out ignore); } public int Count { get { return _internal.Count; } } public bool IsReadOnly { get { return false; } } public IEnumerator GetEnumerator() { return _internal.Keys.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
我刚刚遇到类似的情况(“我对快速添加和包含和删除感兴趣”)并实现了这个吸盘:
using System.Collections.Generic; using System.Threading; namespace BlahBlah.Utilities { public class ConcurrentHashSet : IDisposable { private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); private readonly HashSet _hashSet = new HashSet (); #region Implementation of ICollection ...ish public bool Add(T item) { try { _lock.EnterWriteLock(); return _hashSet.Add(item); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } } public void Clear() { try { _lock.EnterWriteLock(); _hashSet.Clear(); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } } public bool Contains(T item) { try { _lock.EnterReadLock(); return _hashSet.Contains(item); } finally { if (_lock.IsReadLockHeld) _lock.ExitReadLock(); } } public bool Remove(T item) { try { _lock.EnterWriteLock(); return _hashSet.Remove(item); } finally { if (_lock.IsWriteLockHeld) _lock.ExitWriteLock(); } } public int Count { get { try { _lock.EnterReadLock(); return _hashSet.Count; } finally { if (_lock.IsReadLockHeld) _lock.ExitReadLock(); } } } #endregion #region Dispose public void Dispose() { if (_lock != null) _lock.Dispose(); } #endregion } }
没有真正测试过它(性能或可靠性)。 因人而异。
这是基于ConcurrentDictionary
的并发集的实现:
public class ConcurrentSet : IEnumerable , ISet , ICollection { private readonly ConcurrentDictionary _dictionary = new ConcurrentDictionary(); /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// public IEnumerator GetEnumerator() { return _dictionary.Keys.GetEnumerator(); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// Removes the first occurrence of a specific object from the . /// /// /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . /// /// The object to remove from the .The is read-only. public bool Remove(T item) { return TryRemove(item); } /// /// Gets the number of elements in the set. /// public int Count { get { return _dictionary.Count; } } /// /// Gets a value indicating whether the is read-only. /// /// /// true if the is read-only; otherwise, false. /// public bool IsReadOnly { get { return false; } } /// /// Gets a value that indicates if the set is empty. /// public bool IsEmpty { get { return _dictionary.IsEmpty; } } public ICollection Values { get { return _dictionary.Keys; } } /// /// Adds an item to the . /// /// The object to add to the .The is read-only. void ICollection .Add(T item) { if(!Add(item)) throw new ArgumentException("Item already exists in set."); } /// /// Modifies the current set so that it contains all elements that are present in both the current set and in the specified collection. /// /// The collection to compare to the current set. is null. public void UnionWith(IEnumerable other) { foreach (var item in other) TryAdd(item); } /// /// Modifies the current set so that it contains only elements that are also in a specified collection. /// /// The collection to compare to the current set. is null. public void IntersectWith(IEnumerable other) { var enumerable = other as IList ?? other.ToArray(); foreach (var item in this) { if (!enumerable.Contains(item)) TryRemove(item); } } /// /// Removes all elements in the specified collection from the current set. /// /// The collection of items to remove from the set. is null. public void ExceptWith(IEnumerable other) { foreach (var item in other) TryRemove(item); } /// /// Modifies the current set so that it contains only elements that are present either in the current set or in the specified collection, but not both. /// /// The collection to compare to the current set. is null. public void SymmetricExceptWith(IEnumerable other) { throw new NotImplementedException(); } /// /// Determines whether a set is a subset of a specified collection. /// /// /// true if the current set is a subset of ; otherwise, false. /// /// The collection to compare to the current set. is null. public bool IsSubsetOf(IEnumerable other) { var enumerable = other as IList ?? other.ToArray(); return this.AsParallel().All(enumerable.Contains); } /// /// Determines whether the current set is a superset of a specified collection. /// /// /// true if the current set is a superset of ; otherwise, false. /// /// The collection to compare to the current set. is null. public bool IsSupersetOf(IEnumerable other) { return other.AsParallel().All(Contains); } /// /// Determines whether the current set is a correct superset of a specified collection. /// /// /// true if the object is a correct superset of ; otherwise, false. /// /// The collection to compare to the current set. is null. public bool IsProperSupersetOf(IEnumerable other) { var enumerable = other as IList ?? other.ToArray(); return this.Count != enumerable.Count && IsSupersetOf(enumerable); } /// /// Determines whether the current set is a property (strict) subset of a specified collection. /// /// /// true if the current set is a correct subset of ; otherwise, false. /// /// The collection to compare to the current set. is null. public bool IsProperSubsetOf(IEnumerable other) { var enumerable = other as IList ?? other.ToArray(); return Count != enumerable.Count && IsSubsetOf(enumerable); } /// /// Determines whether the current set overlaps with the specified collection. /// /// /// true if the current set and share at least one common element; otherwise, false. /// /// The collection to compare to the current set. is null. public bool Overlaps(IEnumerable other) { return other.AsParallel().Any(Contains); } /// /// Determines whether the current set and the specified collection contain the same elements. /// /// /// true if the current set is equal to ; otherwise, false. /// /// The collection to compare to the current set. is null. public bool SetEquals(IEnumerable other) { var enumerable = other as IList ?? other.ToArray(); return Count == enumerable.Count && enumerable.AsParallel().All(Contains); } /// /// Adds an element to the current set and returns a value to indicate if the element was successfully added. /// /// /// true if the element is added to the set; false if the element is already in the set. /// /// The element to add to the set. public bool Add(T item) { return TryAdd(item); } public void Clear() { _dictionary.Clear(); } public bool Contains(T item) { return _dictionary.ContainsKey(item); } /// /// Copies the elements of the to an , starting at a particular index. /// /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing.The zero-based index in at which copying begins. is null. is less than 0. is multidimensional.-or-The number of elements in the source is greater than the available space from to the end of the destination .-or-Type cannot be cast automatically to the type of the destination . public void CopyTo(T[] array, int arrayIndex) { Values.CopyTo(array, arrayIndex); } public T[] ToArray() { return _dictionary.Keys.ToArray(); } public bool TryAdd(T item) { return _dictionary.TryAdd(item, default(byte)); } public bool TryRemove(T item) { byte donotcare; return _dictionary.TryRemove(item, out donotcare); } }
ConcurrentDictionary具有更好的性能特征,因为它使用无锁方式进行读取(至少在.NET 4.0+中)。 因此,对于重度multithreading场景中的性能,ConcurrentDictionary可能作为readerwriterlockslim包装器表现更好。 但是你需要携带一个空字节作为虚拟值(我同意,这看起来很糟糕)。
你打算用它做什么?
即使您确实使用了这些方法,也可以使用以下方案:
var set1 = new ConcurrentHashSet(); ... if (set1.Overlaps(set2)) { set1.IntersectWith(set2); assert(! set1.IsEmpty()); // might fail }
这可能是可以接受的,但Set在并发设置中比在队列中更不可能有用。