并发散列集<T>在 .NET 框架中? [英] Concurrent HashSet<T> in .NET Framework?
问题描述
我有以下课程.
class Test{
public HashSet<string> Data = new HashSet<string>();
}
我需要更改来自不同线程的数据"字段,因此我想对我当前的线程安全实现提出一些意见.
I need to change the field "Data" from different threads, so I would like some opinions on my current thread-safe implementation.
class Test{
public HashSet<string> Data = new HashSet<string>();
public void Add(string Val){
lock(Data) Data.Add(Val);
}
public void Remove(string Val){
lock(Data) Data.Remove(Val);
}
}
是否有更好的解决方案,直接进入字段并保护它免受多个线程的并发访问?
Is there a better solution, to go directly to field and protect it from concurrent access by multiple threads?
推荐答案
你的实现是正确的.不幸的是,.NET Framework 不提供内置的并发散列集类型.但是,有一些解决方法.
Your implementation is correct. The .NET Framework does not provide a built-in concurrent hashset type, unfortunately. However, there are some workarounds.
ConcurrentDictionary(推荐)
第一个是使用名称空间System.Collections.Concurrent
中的类ConcurrentDictionary
.在这种情况下,该值毫无意义,因此我们可以使用一个简单的 byte
(内存中的 1 个字节).
This first one is to use the class ConcurrentDictionary<TKey, TValue>
in the namespace System.Collections.Concurrent
. In the case, the value is pointless, so we can use a simple byte
(1 byte in memory).
private ConcurrentDictionary<string, byte> _data;
这是推荐的选项,因为该类型是线程安全的,并且为您提供与 HashSet
相同的优点,但键和值是不同的对象.
This is the recommended option because the type is thread-safe and provide you the same advantages than a HashSet<T>
except key and value are different objects.
来源:社交 MSDN
ConcurrentBag
如果你不介意重复的条目,你可以在上一个类的相同命名空间中使用 ConcurrentBag
类.
If you don't mind about the duplicate entries, you can use the class ConcurrentBag<T>
in the same namespace of the previous class.
private ConcurrentBag<string> _data;
自行实现
最后,正如您所做的那样,您可以使用锁或 .NET 为您提供的其他线程安全方式来实现您自己的数据类型.这是一个很好的例子:如何在 .Net 中实现 ConcurrentHashSet
Finally, as you did, you can implement your own data type, using lock or other ways that the .NET provides you to be thread-safe. Here is a great example: How to implement ConcurrentHashSet in .Net
这个解决方案的唯一缺点是类型 HashSet
没有正式的并发访问,即使是读取操作.
The only drawback of this solution is that the type HashSet<T>
doesn't officially concurrent access, even for reading operations.
我引用了链接帖子的代码(最初由 Ben Mosher 编写).
I quote the code of the linked post (originally written by Ben Mosher).
using System;
using System.Collections.Generic;
using System.Threading;
namespace BlahBlah.Utilities
{
public class ConcurrentHashSet<T> : IDisposable
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
private readonly HashSet<T> _hashSet = new HashSet<T>();
#region Implementation of ICollection<T> ...ish
public bool Add(T item)
{
_lock.EnterWriteLock();
try
{
return _hashSet.Add(item);
}
finally
{
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
}
}
public void Clear()
{
_lock.EnterWriteLock();
try
{
_hashSet.Clear();
}
finally
{
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
}
}
public bool Contains(T item)
{
_lock.EnterReadLock();
try
{
return _hashSet.Contains(item);
}
finally
{
if (_lock.IsReadLockHeld) _lock.ExitReadLock();
}
}
public bool Remove(T item)
{
_lock.EnterWriteLock();
try
{
return _hashSet.Remove(item);
}
finally
{
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
}
}
public int Count
{
get
{
_lock.EnterReadLock();
try
{
return _hashSet.Count;
}
finally
{
if (_lock.IsReadLockHeld) _lock.ExitReadLock();
}
}
}
#endregion
#region Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
if (_lock != null)
_lock.Dispose();
}
~ConcurrentHashSet()
{
Dispose(false);
}
#endregion
}
}
将入口锁方法移到 try
块之外,因为它们可能引发异常并执行 finally
中包含的指令块.
Move the entrance lock methods ouside the try
blocks, as they could throw an exception and execute the instructions contained in the finally
blocks.
这篇关于并发散列集<T>在 .NET 框架中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!