Estoy luchando con este problema y realmente agradecería cualquier ayuda. Estoy trabajando en un proyecto existente. He agregado una lógica que cuenta las combinaciones de valores y se asegura de que no pasemos algún límite. Por ejemplo, dadas las columnas de esta tabla de datos:
Name|Age|description
El código se asegura de que no tengamos más de K combinaciones de Nombre, Edad. Tengo datos que contienen algo así como un millón de pares como este. En algún momento, el programa simplemente falla o se atasca, aunque no veo ningún problema de memoria o de CPU. Implementé este límite usando ConcurrentDictionary
of tuplas (Name, Age) como claves, y estoy usando C# .NET 6 ..
Puedo ver que el tiempo que lleva intentar agregar un elemento al DS se vuelve realmente largo en algún momento.
Editar: agregar algunas piezas de código, aunque es una gran cantidad de implementación interna, creo que estas son las partes principales del código para comprender el problema:
aquí está el componente que es responsable de limitar las claves:
protected override Result Process(Row row) { var valueToLimit = GetValueToLimit(row); var result = _values.TryAdd(valueToLimit); } // some logic related to the case of crossing the limit return Result.Success; } protected abstract T GetValueToLimit(Row row); }
La función GetValueToLimit está implementada para mi caso:
protected override string[] GetValueToLimit(Row row) { // takes the relevant values from an input record, according to the requested columns. return _columnIndices.Select(x => row.GetValue(x)).ToArray(); }
y finalmente, aquí hay algunas partes de la implementación simultánea de HashSet:
public class BoundedConcurrentHashSet<K> : ConcurrentHashSet<K> { .. public override Result TryAdd(K element) { if (Dictionary.Count() < _maxCapacity) { return base.TryAdd(element); } else { return Contains(element) ? Result.AlreadyInHash : Result.ExceedsCapacity; } }
donde concurrentHashSet se implementa con C# concurrentDictionary:
public class ConcurrentHashSet<K> { public ConcurrentHashSet(IEqualityComparer<K> equalityComparer) { Dictionary = new ConcurrentDictionary<K, object>(equalityComparer); } protected ConcurrentDictionary<K, object> Dictionary { get; } public int Count => Dictionary.Count; public IEnumerable<K> Elements => Dictionary.Keys; public virtual Result TryAdd(K element) { return Dictionary.TryAdd(element, null) ? dResult.Added : Result.AlreadyInHash; } public bool Contains(K element) { return Dictionary.ContainsKey(element); }
Por favor, comparta cualquier idea que pueda ayudar.
Gracias
Aquí está tu problema:
public override ConcurrentHashSetAddResult TryAdd(K element) { if (Dictionary.Count() < _maxCapacity) { return base.TryAdd(element); } //...
...donde Dictionary
es el ConcurrentDictionary<K, object>
subyacente.
Count()
es un método LINQ que enumera la secuencia enumerable de principio a fin o devuelve la propiedad Count
en caso de que la secuencia implemente la ICollection<TSource>
. El ConcurrentDictionary<K, V>
implementa esta interfaz, por lo que la propiedad Count
se usa de hecho. Esto es lo que dice la documentación de esta propiedad :
Esta propiedad tiene una semántica de instantánea y representa la cantidad de elementos en
ConcurrentDictionary<TKey,TValue>
en el momento en que se accedió a la propiedad.
La "semántica instantánea" es la parte importante. Significa que para adquirir el Count
, el diccionario debe estar completamente bloqueado temporalmente. Cuando un subproceso lee Count
, todos los demás subprocesos tienen que esperar. Sin concurrencia en absoluto.
En algún momento se propuso una propiedad ApproximateCount
en GitHub, pero no tuvo suficiente tracción y ahora está cerrada. Esa propiedad le permitiría implementar la funcionalidad BoundConcurrentHashSet
con una sobrecarga muy reducida, pero también con un comportamiento menos preciso: sería posible superar la configuración de _maxCapacity
.
Mi sugerencia es deshacerse de ConcurrentDictionary<K, object>
y usar un HashSet<T>
como almacenamiento subyacente, protegido con un lock
.