Pregunta

Mi equipo está actualmente debatiendo este tema.

El código en cuestión es algo así como

if (!myDictionary.ContainsKey(key))
{
    lock (_SyncObject)
    {
        if (!myDictionary.ContainsKey(key))
        {
            myDictionary.Add(key,value);
        }
    }
}

Algunas de las publicaciones que he visto dicen que esto puede ser un gran NO NO (cuando se usa TryGetValue).Sin embargo, los miembros de nuestro equipo dicen que está bien ya que "ContainsKey" no itera en la colección de claves, sino que verifica si la clave está contenida mediante el código hash en O(1).Por eso afirman que aquí no hay peligro.

Me gustaría recibir sus opiniones honestas sobre este tema.

¿Fue útil?

Solución

No hagas esto.No es seguro.

podrías estar llamando ContainsKey de un hilo mientras otro hilo llama Add.Eso simplemente no está respaldado por Dictionary<TKey, TValue>.Si Add necesita reasignar depósitos, etc., te imagino podría Obtenga algunos resultados muy extraños o una excepción.Él puede Se han escrito de tal manera que no se ven efectos desagradables, pero no me gustaría confiar en ello.

Una cosa es usar un bloqueo de doble verificación para lecturas/escrituras simples en un campo, aunque todavía estaría en contra de ello; otra es realizar llamadas a una API que se ha descrito explícitamente como no siendo seguro para múltiples llamadas simultáneas.

Si estás en .NET 4, ConcurrentDictionary Este es probablemente el camino a seguir.De lo contrario, simplemente bloquee cada acceso.

Otros consejos

Si está en un entorno multithreado, es posible que prefiera mirar usando un concurrente.Lo blogueé hace un par de meses, puede encontrar el artículo útil: http://colinmackay.co.uk/blog/2011/03/24/Parallelisation-in-net-4-0-El-ConCurrent-Dictionary/

Este código es incorrecto.El Dictionary<TKey, TValue> El tipo no admite operaciones simultáneas de lectura y escritura.Aunque tu Add El método se llama dentro de la cerradura. ContainsKey no es.Por lo tanto, permite fácilmente una violación de la regla de lectura/escritura simultánea y provocará corrupción en su instancia.

No parece seguro de hilo, pero probablemente sería difícil hacer que falle.

El argumento de búsqueda de iteración vs hash no se mantiene, podría haber una colisión hash, por ejemplo.

Si este diccionario rara vez se escribe y, a menudo, lee, luego a menudo empleo un doble bloqueo seguro reemplazando a todo el diccionario en la escritura. Esto es particularmente eficaz si puede escribirlos para lotes para hacerlos menos frecuentes.

Por ejemplo, esta es una versión reducida de un método que usamos que intenta obtener un objeto de esquema asociado con un tipo, y si no puede, adelante, se adelante y crea objetos de esquema para todos los tipos que encuentra. En el mismo conjunto que el tipo especificado para minimizar la cantidad de veces que se debe copiar todo el diccionario:

    public static Schema GetSchema(Type type)
    {
        if (_schemaLookup.TryGetValue(type, out Schema schema))
            return schema;

        lock (_syncRoot) {
            if (_schemaLookup.TryGetValue(type, out schema))
                return schema;

            var newLookup = new Dictionary<Type, Schema>(_schemaLookup);

            foreach (var t in type.Assembly.GetTypes()) {
                var newSchema = new Schema(t);
                newLookup.Add(t, newSchema);
            }

            _schemaLookup = newLookup;

            return _schemaLookup[type];
        }
    }

Así que el diccionario en este caso será reconstruido, a lo sumo, tantas veces, ya que hay asambleas con tipos que necesitan esquemas. Para el resto de la vida útil de la aplicación, los accesos de diccionario estarán libres de bloqueo. La copia del diccionario se convierte en un costo de inicialización de una sola vez del ensamblaje. El intercambio de diccionarios es seguro de hilo, ya que las escrituras de puntero son atómicas para que toda la referencia se cambia a la vez.

También puede aplicar principios similares en otras situaciones.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top