Su implementación es correcta. Desafortunadamente, .NET Framework no proporciona un tipo de hashset concurrente incorporado. Sin embargo, hay algunas soluciones alternativas.
ConcurrentDictionary (recomendado)
El primero es usar la clase ConcurrentDictionary<TKey, TValue>
en el espacio de nombres System.Collections.Concurrent
. En el caso, el valor no tiene sentido, por lo que podemos usar un simple byte
(1 byte en memoria).
private ConcurrentDictionary<string, byte> _data;
Esta es la opción recomendada porque el tipo es seguro para subprocesos y le brinda las mismas ventajas que una HashSet<T>
clave, excepto que el valor son objetos diferentes.
Fuente: Social MSDN
Bolsa concurrente
Si no le importan las entradas duplicadas, puede usar la clase ConcurrentBag<T>
en el mismo espacio de nombres de la clase anterior.
private ConcurrentBag<string> _data;
Auto-implementación
Finalmente, como lo hizo, puede implementar su propio tipo de datos, utilizando el bloqueo u otras formas en que .NET le brinda seguridad para subprocesos. Aquí hay un gran ejemplo: Cómo implementar ConcurrentHashSet en .Net
El único inconveniente de esta solución es que el tipo HashSet<T>
no tiene acceso oficialmente concurrente, incluso para operaciones de lectura.
Cito el código de la publicación vinculada (originalmente escrita por 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
}
}
EDITAR: Mueva los métodos de bloqueo de entrada fuera de los try
bloques, ya que podrían lanzar una excepción y ejecutar las instrucciones contenidas en los finally
bloques.
System.Collections.Concurrent