L'implementazione è corretta. Purtroppo, .NET Framework non fornisce un tipo di hashset simultaneo incorporato. Tuttavia, ci sono alcune soluzioni alternative.
ConcurrentDictionary (consigliato)
Il primo è usare la classe ConcurrentDictionary<TKey, TValue>
nello spazio dei nomi System.Collections.Concurrent
. Nel caso, il valore è inutile, quindi possiamo usare un semplice byte
(1 byte in memoria).
private ConcurrentDictionary<string, byte> _data;
Questa è l'opzione consigliata perché il tipo è thread-safe e offre gli stessi vantaggi di una HashSet<T>
chiave tranne e il valore sono oggetti diversi.
Fonte: Social MSDN
ConcurrentBag
Se non ti dispiace per le voci duplicate, puoi usare la classe ConcurrentBag<T>
nello stesso spazio dei nomi della classe precedente.
private ConcurrentBag<string> _data;
Self-implementazione
Infine, come hai fatto, puoi implementare il tuo tipo di dati, usando il blocco o altri modi che .NET ti fornisce per essere thread-safe. Ecco un ottimo esempio: come implementare ConcurrentHashSet in .Net
L'unico inconveniente di questa soluzione è che il tipo HashSet<T>
non ha ufficialmente un accesso simultaneo, anche per le operazioni di lettura.
Cito il codice del post collegato (originariamente scritto da 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
}
}
EDIT: sposta i metodi di blocco dell'entrata fuori dai try
blocchi, poiché potrebbero generare un'eccezione ed eseguire le istruzioni contenute nei finally
blocchi.
System.Collections.Concurrent